name: offline-first-development
description: Build offline-capable applications with IndexedDB, service workers, background sync, and conflict resolution patterns.
version: "1.0.0"
last-updated: "2026-04-17"
model_tested: "claude-sonnet-4-6"
category: offline
platforms: [claude-code, codex, gemini-cli, cursor, copilot, windsurf, cline]
language: en
geo_relevance: [global, africa]
priority: medium
dependencies:
mcp: []
skills: []
apis: []
data: []
update_sources:
- url: "https://web.dev/articles/offline-cookbook"
check_frequency: "yearly"
last_checked: "2026-04-21"
license: MIT
Offline-First Development
When to Use
- Building apps for low-connectivity environments (rural, Africa, transit)
- Building PWAs that must work without internet
- Designing sync strategies for mobile apps
- Implementing local-first architectures
- Building for environments with unreliable connections
Core Principles
- Local-first: Data lives on the device. Server is backup, not source of truth.
- Sync on reconnect: Queue changes offline, sync when connection returns.
- Conflict resolution: Define merge strategy before building.
- Progressive enhancement: App works offline; online adds features.
Storage Options
| Storage | Size Limit | Persistence | Best For |
|---|
| IndexedDB | ~50% of disk | Persistent | Structured data, large datasets |
| Cache API | ~50% of disk | Persistent | HTTP responses, assets |
| localStorage | 5-10 MB | Persistent | Small key-value config |
| OPFS | ~50% of disk | Persistent | File-like access (new) |
Recommendation: IndexedDB for data, Cache API for assets, OPFS for files.
Service Worker Pattern
Install → Cache essential assets (app shell, fonts, icons)
Activate → Clean old caches
Fetch → Cache-first for assets, network-first for API data
Sync → Background sync for queued mutations
Caching Strategies
| Strategy | When | How |
|---|
| Cache-first | Static assets (CSS, JS, images) | Serve from cache, update in background |
| Network-first | API data | Try network, fall back to cache |
| Stale-while-revalidate | Semi-static (user profile) | Serve cache, update from network |
| Network-only | Real-time data (chat, payments) | No caching |
Sync Patterns
Optimistic Updates
- Apply change locally immediately
- Queue mutation for sync
- On reconnect: send queued mutations
- On conflict: apply resolution strategy
Conflict Resolution Strategies
| Strategy | When | How |
|---|
| Last-write-wins | Simple data, single user | Timestamp comparison |
| Server-wins | Authoritative server | Discard local on conflict |
| Client-wins | User autonomy priority | Overwrite server |
| Merge | Collaborative editing | Field-level merge |
| Manual | Critical data | Show conflict to user |
Queue Design
Mutation queue entry:
{
id: "uuid",
timestamp: "ISO-8601",
entity: "task",
entity_id: "uuid",
action: "create|update|delete",
payload: { ... },
retries: 0,
status: "pending|syncing|failed|synced"
}
Connectivity Detection
// Basic
navigator.onLine // boolean (unreliable alone)
// Robust: combine with actual fetch test
async function isOnline() {
try {
const response = await fetch('/api/health', {
method: 'HEAD',
cache: 'no-store',
signal: AbortSignal.timeout(3000)
});
return response.ok;
} catch {
return false;
}
}
Mobile-Specific Considerations
- Battery: Batch sync operations, avoid polling
- Bandwidth: Compress payloads, delta sync
- Storage quotas: Monitor usage, evict old data
- App lifecycle: Handle background/foreground transitions
What This Skill Does NOT Do
- Does not implement a specific sync protocol (CRDT, OT)
- Does not handle real-time collaboration (use dedicated tools)
- Does not manage authentication tokens offline (security concern)
- Does not provide a database library (guides architecture choices)