The Problem Nobody Talks About
When I first started building apps for Zimbabwe, I made a critical mistake: I assumed connectivity. My first app required constant internet. It failed.
In emerging markets, network conditions are unpredictable. Users switch between 2G, 3G, and WiFi constantly. Data is expensive—$5 per GB in Zimbabwe. And yet, most tutorials assume you're building for San Francisco.
What Offline-First Actually Means
Offline-first isn't about caching. It's a fundamental shift in how you think about data:
- Local-first: All data lives on the device first
- Sync when possible: Background sync when network appears
- Conflict resolution: Handle what happens when two devices edit the same thing
- Progressive enhancement: Add online features as connectivity allows
The best offline app is one where users don't know they're offline.
The Architecture Pattern I Use
Here's the stack I've standardized on for every project:
1. IndexedDB for Local Storage
SQLite alternatives exist, but IndexedDB works in browsers and PWAs without additional dependencies:
// Using Dexie.js for IndexedDB
const db = new Dexie('FareWise');
db.version(1).stores({
trips: '++id, route, platform, fare, synced',
settings: 'key, value'
});
2. Service Workers for Caching
Every asset should be available offline:
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then((cache) => {
return cache.addAll([
'/',
'/styles.css',
'/app.js',
'/icons/icon-192.png'
]);
})
);
});
3. Background Sync for Data
When the network returns, push pending changes:
async function syncPendingTrips() {
const unsyncedTrips = await db.trips
.where('synced')
.equals(false)
.toArray();
for (const trip of unsyncedTrips) {
try {
await api.createTrip(trip);
await db.trips.update(trip.id, { synced: true });
} catch (err) {
// Will retry on next sync
console.log('Sync failed, will retry');
}
}
}
Real Results
This pattern powers FareWise, which works 100% offline. Drivers calculate fares with zero connectivity, and data syncs when they're back in range.
The key metrics:
- 0 bytes required for core functionality
- < 50KB initial bundle (compressed)
- Sub-second load on 2G connections
The Takeaway
If you're building for emerging markets, start offline-first. Don't retrofit it later. The architecture decisions you make on day one determine whether your app works in the real world.
