Offline-first and sync

How should an app show sync status?

An app should show sync status in clear words, on the items it applies to. It should say what is saved on this device, what is still waiting to send, what another device or the server has confirmed, and what failed along with how to fix it. The data should stay usable while it syncs, with progress indicators kept for work the person is actually waiting on.

Learning objectives

After reading this article you will be able to:

  • Explain why a single online or offline badge misleads people
  • List the sync states worth showing on each item
  • Describe how to show progress and failures without blocking work

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:

StateWhat it meansHow it might look
Saved on this deviceCommitted to local storageShown normally, with a quiet pending mark
Waiting to sendQueued, no path yetThe pending mark, plus a count somewhere central
Reached another deviceDelivered, not yet acted onA sent or delivered mark, if the app tracks it
ConfirmedAccepted by the app or server that decidesThe pending mark disappears
Needs attentionRejected, expired, or failed to saveA 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.

Frequently asked questions

Should an offline app show a banner when there is no connection?

A small, persistent indicator is useful, but it should not be the only signal. What people need to know is whether their own work is safe and whether it has reached others, which is a property of each item rather than of the connection.

Is a spinning sync icon enough?

Rarely. Apple's guidance points out that an indeterminate indicator shows something is happening but not how long it will take, and recommends avoiding vague labels. A count of items waiting and the time of the last successful sync say more.

Sources

Build it with Offline Protocol

The application state and receipts page asks for separate indicators for locally saved work, peer delivery, application acceptance and backend commit, keeps pending work visible during disconnection, and pairs expiry, rejection and storage failures with a recovery action.

Read application state and receipts