A demonstration of an offline-first mobile application built for Package.AI, showcasing queue management, request prioritization, and automatic synchronization when connectivity is restored.
This app simulates a delivery driver's mobile application that continues to work seamlessly even in areas with poor or no cellular reception. All user actions are queued locally and automatically synced to the backend when connectivity is restored, with intelligent prioritization of small requests over large ones.
- Offline-First Architecture: All actions work immediately, even when offline
- Request Prioritization: Small requests (status updates) are sent before large requests (image uploads)
- Flexible Sync Modes: Segmented control to switch between automatic and manual sync modes
- Persistent Storage: All queued actions persist between app restarts using AsyncStorage
- Exponential Backoff: Smart retry mechanism with exponential backoff for failed requests
- Real-time Status: Network status indicator and live queue statistics
- Success Logs: Visual log of all successfully synced requests with timestamps
- Dark Mode Support: Full support for light and dark themes
- Pending Queue View: Real-time view of pending tasks with processing status
- Intuitive UI: Clear, tappable buttons with visual feedback for all actions
- Node.js (v18 or higher)
- npm or yarn
- Expo CLI
- iOS Simulator (Mac) or Android Emulator, or physical device with Expo Go app
- Install dependencies:
npm install- Start the development server:
npm start- Run on your preferred platform:
# iOS (Mac only)
npm run ios
# Android
npm run android- Launch the app on your device or simulator
- Verify you're in Auto mode (tap โฎ menu to check - "๐ Auto" should be selected)
- Enable Airplane Mode to simulate offline conditions
- Tap the "SMALL" and "LARGE" buttons to queue requests
- Observe the pending queue updating in real-time
- Disable Airplane Mode to restore connectivity
- Watch as the app automatically syncs all pending requests
- Notice small requests are processed first, followed by large requests
- View the success logs with timestamps
- Close and reopen the app to verify persistence
- Tap the โฎ button in the top-right corner to open Settings
- Select "๐ Manual" mode in the Sync Mode section
- Close the settings menu
- Enable Airplane Mode and add requests
- Disable Airplane Mode - requests will NOT auto-sync
- Tap "Sync Now" button to manually trigger synchronization
- Open Settings and switch back to Auto Mode to see immediate sync
Tap the โฎ button in the top-right corner to access:
- Sync Mode: Toggle between Auto and Manual sync
- Toggle Theme: Switch between Light and Dark mode
- Clear Storage: Delete all queued requests and success logs
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ React UI Layer โ
โ (index.tsx + useOfflineQueue hook) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Service Layer โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ SyncService โ โ QueueService โ โ ApiService โ โ
โ โ (Network โ โ (Priority โ โ (Mock โ โ
โ โ Monitoring) โ โ Queue) โ โ Backend) โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโดโโโโโโโโโโ โ
โ โ StorageService โ โ
โ โ (AsyncStorage)โ โ
โ โโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- Rationale: Simpler setup, sufficient for demo purposes
- Trade-off: SQLite would be better for production with larger datasets
- Benefit: Zero native dependencies, works out of the box
- Rationale: Fast access with guaranteed persistence
- Implementation: Queue stored in-memory, synced to AsyncStorage on every change
- Benefit: Best performance while ensuring data survives app restarts
- Rationale: Prevents overwhelming the network/backend
- Parameters: 1s โ 2s โ 4s (with 10s cap)
- Benefit: Graceful handling of temporary network issues
- Rationale: Prioritize time-sensitive small requests
- Implementation: Single queue, sorted by priority at processing time
- Benefit: Simpler code, maintains order within priority levels
- Rationale: Centralized user preferences (theme, sync mode) without prop drilling
- Implementation:
PreferencesContextprovides global access to settings - Benefit: Clean component signatures, single source of truth
For apps with more complex state requirements, consider these alternatives:
| Tool | Best For | Pros | Cons |
|---|---|---|---|
| Redux | Large teams, complex state logic | Time-travel debugging, DevTools, predictable updates | Boilerplate-heavy, learning curve |
| MobX | Reactive UIs, rapid development | Less boilerplate, automatic updates | Less predictable, harder to debug |
| Zustand | Simple global state | Minimal API, no Provider needed | Limited ecosystem, fewer tools |
| Recoil | Derived state, atom-based patterns | Fine-grained reactivity, Facebook-backed | Newer, smaller community |
| Jotai | Atomic state management | Lightweight, TypeScript-first | Smaller ecosystem |
Why Context API here?
- Only 2-3 settings to manage
- Infrequent updates (user rarely changes preferences)
- No complex state derivations
- No need for middleware or dev tools
- Perfect for small-to-medium apps
- Rationale: Realistic testing without backend dependency
- Features: Adjustable delays and failure rates
- Benefit: Easy to test various network conditions
- Rationale: Clean separation between services and UI
- Implementation: Services notify subscribers of changes
- Benefit: React components automatically re-render on state changes
- โ Add small request โ Appears in queue
- โ Add large request โ Appears in queue
- โ Online sync โ Requests processed in correct order
- โ Offline โ Small request โ Stays in queue
- โ Offline โ Large request โ Stays in queue
- โ Multiple offline requests โ All queued correctly
- โ Go online โ Auto-sync triggered
- โ Small requests processed before large requests
- โ Failed requests retry with backoff
- โ Success logs updated after completion
- โ Queue persists after app restart
- โ Success logs persist after app restart
- โ Network state detected on app launch
Access the Settings menu (โฎ button in top-right) to switch between sync modes in real-time - no code changes needed!
๐ Auto Mode (Default):
- โ Requests sent immediately when online
- โ Queued when offline
- โ Auto-sync when connection restored (offline โ online)
- โ No manual "Sync Now" button (handled automatically)
- Best for: Production use, delivery driver apps, seamless UX
- How to access: Settings โ Sync Mode โ Select "๐ Auto"
๐ Manual Mode:
- โ Requests sent immediately when online
- โ Queued when offline
- โ NO auto-sync when connection restored
- โ "Sync Now" button appears to manually trigger sync
- Best for: User control, testing, demo purposes
- How to access: Settings โ Sync Mode โ Select "๐ Manual"
Note: Both modes send requests immediately when already online. The mode selection only affects behavior when transitioning from offline to online.
Edit services/api.service.ts to adjust mock API behavior:
const API_CONFIG = {
SMALL_REQUEST_DELAY: 500, // Delay for small requests (ms)
LARGE_REQUEST_DELAY: 2000, // Delay for large requests (ms - simulates image upload)
FAILURE_RATE: 0, // API failure rate (0 = disabled, 0.1 = 10% chance)
};Note: FAILURE_RATE is set to 0 by default. Set to 0.1 to simulate network failures and test retry logic.
Edit services/queue.service.ts to adjust retry behavior:
const RETRY_CONFIG = {
MAX_RETRIES: 3, // Maximum retry attempts per request
INITIAL_BACKOFF_MS: 1000, // Initial backoff delay (1 second)
MAX_BACKOFF_MS: 10000, // Maximum backoff delay (10 seconds)
};Note: Exponential backoff: 1s โ 2s โ 4s โ 8s (capped at 10s)
app/
โโโ _layout.tsx # Root layout with PreferencesProvider
โโโ index.tsx # Main screen
components/queue/ # Modular UI components
โโโ action-buttons.tsx # Small/Large request buttons
โโโ failed-tasks-card.tsx # Failed tasks with retry
โโโ instructions-card.tsx # Testing instructions
โโโ pending-queue-card.tsx # Pending/processing queue
โโโ queue-stats-card.tsx # Stats + Sync Now button
โโโ settings-modal.tsx # Settings menu
โโโ status-card.tsx # Network status indicator
โโโ success-logs-card.tsx # Completed tasks log
โโโ task-item.tsx # Reusable task item component
contexts/
โโโ preferences-context.tsx # User preferences (theme, sync mode)
hooks/
โโโ use-offline-queue.ts # Queue operations interface
services/
โโโ api.service.ts # Mock API (500ms/2s delays)
โโโ queue.service.ts # Priority queue + retry logic
โโโ storage.service.ts # AsyncStorage wrapper
โโโ sync.service.ts # Network monitoring
types/
โโโ queue.types.ts # TypeScript interfaces
utils/
โโโ format.utils.ts # Date/time formatting
- Ensure you've granted network permissions to the app
- Network status updates automatically when connection changes
- On iOS simulator: Hardware โ Network Link Conditioner or Features โ Airplane Mode
- On Android emulator: Use the network toggle in extended controls
- Verify you're in Auto Mode (toggle should be OFF)
- Check console logs for errors
- Ensure device has internet connectivity
- Try toggling Airplane Mode off/on
- Verify you're in Manual Mode (toggle should be ON)
- Ensure "Sync Now" button is visible (only shows when online with pending items)
- Check console for error messages
- Clear node_modules:
rm -rf node_modules && npm install - Clear Metro cache:
npm start -- --clear - Clear app data on device/simulator and restart
- โ Offline-first queue with persistence
- โ Priority-based request processing (small โ large)
- โ Exponential backoff retry mechanism
- โ Automatic and manual sync modes
- โ Real-time UI updates with observer pattern
- โ Network connectivity monitoring
- โ AsyncStorage for persistent state
- โ Dark mode support
- โ Task status tracking (pending, processing, completed)
- โ Success logs with timestamps and task IDs
- SQLite for better performance with large queues
- Request deduplication to prevent duplicate submissions
- Multiple priority levels (urgent, normal, low)
- Background sync using background tasks API
- Conflict resolution for concurrent edits
- Batch API calls for improved efficiency
- Queue size limits and overflow handling
- Advanced retry strategies (circuit breaker, jitter)
- Analytics and monitoring dashboard
- Offline data caching and preloading