Offline UX
Offline UX is not a banner that says “offline.” It is the product contract for what is saved, what is pending, what failed, and what the user can do next.
Required States
| State | User copy | App behavior |
|---|---|---|
| saved locally | ”Saved on this device” | no network required |
| pending sync | ”Saved locally, waiting to sync” | keep mutation in durable queue |
| failed sync | ”Saved locally, sync failed” | show retry and reason |
| conflict | ”Needs your choice” | preserve both versions |
| storage pressure | ”Storage is almost full” | offer export, cleanup, and retry |
| stale data | ”Last updated 3 days ago” | show last successful sync/fetch |
UX Rules
- Confirm local save before network save.
- Show pending state near the affected object, not only in a global status icon.
- Let users retry failed sync without duplicating actions.
- Keep destructive restore/import behind a dry-run or confirmation.
- Make export obvious before users need it.
- Explain degraded mode in product language.
Queue Status Example
type SyncStatus =
| { state: "local-only" }
| { state: "pending"; count: number }
| { state: "syncing"; count: number }
| { state: "failed"; count: number; lastError: string }
| { state: "conflict"; count: number };
Trust Copy
Use exact words:
- “Your data stays on this device.”
- “Export CSV anytime.”
- “Backup is encrypted before it leaves this device.”
- “Sync failed, but your local changes are safe.”
Avoid vague words:
- “seamless”
- “cloud-safe”
- “automatically protected”
- “never lose data” unless restore gates prove it