Why one online badge is not enough
The simplest sync indicator is a badge that says online or offline. It answers the wrong question. A person who has just recorded an inspection wants to know whether that inspection is safe and whether their supervisor can see it. The state of the network is only a rough guide to either.
The gap grows when devices sync with each other as well as with a server. The Ink & Switch essay on local-first software, reporting on its prototypes, found that peer-to-peer systems are never fully online or offline, and that it can be hard to reason about how data moves in them. A phone can be offline from the internet and fully in step with the colleague next to it, or online and still waiting for a server that is down.
Eric Brewer described the underlying problem in 2012: when a system keeps working through a network split, the user interface has to communicate that tasks are in progress but not complete. He cites the calendar application of Bayou, a research system, which showed potentially inconsistent, tentative entries in a different colour.
The states worth showing
Base the indicators on what the app actually knows, not on what it hopes. For an offline-capable app, that comes down to a handful of states:
| State | What it means | How it might look |
|---|---|---|
| Saved on this device | Committed to local storage | Shown normally, with a quiet pending mark |
| Waiting to send | Queued, no path yet | The pending mark, plus a count somewhere central |
| Reached another device | Delivered, not yet acted on | A sent or delivered mark, if the app tracks it |
| Confirmed | Accepted by the app or server that decides | The pending mark disappears |
| Needs attention | Rejected, expired, or failed to save | A clear message and an action to fix it |
Not every app needs every row. A notes app with no backend rules may only need saved, waiting and confirmed. An app that hands work between people needs to separate delivered from accepted, because a delivered message is not an accepted one.
Mark the item, not just the screen
Sync status is a property of each record, so show it there. Libraries make this possible. Cloud Firestore attaches a hasPendingWrites flag to each document, which stays true until the backend has the local change. It also reports fromCache on each snapshot: when true, the data came from the local cache and might be stale or incomplete.
Those two flags map onto two different messages. Pending writes mean “your change has not reached the server yet”. Data from the cache means “you may not be seeing other people’s latest changes”. An app that shows only the first can mislead someone into acting on old information.
A timestamp can say more than an icon. Apple’s guidance on refresh controls gives the example of Podcasts using the control’s title to tell people when the last update occurred. A line giving the time of the last successful sync is concrete in a way that a spinning symbol is not.
Keep the data usable while it syncs
Sync should not block work. Apple’s Human Interface Guidelines on loading advise showing something as soon as possible, using placeholders while content loads, and letting people do other things while they wait. The local-first ideals put it more strongly: the first is “no spinners: your work at your fingertips”, because local data never needs to wait for a server. The seven ideals explain the reasoning.
When progress is worth showing, Apple’s progress indicator guidance applies directly to sync:
- Prefer a determinate indicator, such as a count of items sent out of the total, when the app knows the size of the job.
- Avoid vague labels such as “loading”, which seldom add value.
- Keep the indicator moving, because people read a stationary one as a stalled or frozen app.
- Show it in a consistent place, so people know where to look.
Failures need a way forward
The states that matter most are the ones where something went wrong. A rejected change, an item that expired before it could be delivered, or a write that failed because storage is full all need a visible message on the affected item, in plain words, with an action: retry, edit and resubmit, free up space, or discard.
Apple’s guidance makes the same point about stalled processes: provide feedback that helps people understand the problem and what they can do about it. Do not hide pending or failed work when the connection drops, and do not clear it silently when it returns. Someone who closed the app with unsent reports should open it to the same reports, still marked.
A quick check
Before shipping, ask whether a person can answer four questions from the screen alone: is my work saved, has it left this device, has the system that decides accepted it, and if not, what should I do? If any answer needs guesswork, the status design is not finished.