[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › [local‑first/Shared](https://evolu.dev/docs/api-reference/common/local-first/Shared) › OwnerSyncStatus

```ts
type OwnerSyncStatus = NoRelaysSyncStatus | RelaySyncStatus;
```

Defined in: [packages/common/src/local-first/Shared.ts:865](https://github.com/evoluhq/evolu/blob/f082fdd974e25fa0eb1134e4b34f15b740af8fe2/packages/common/src/local-first/Shared.ts#L865)

What an app shows users about syncing one owner of one database, from
[syncStateToOwnerSyncStatus](https://evolu.dev/docs/api-reference/common/local-first/Shared/functions/syncStateToOwnerSyncStatus) or the React and Vue `useOwnerSyncStatus`.

Its variants:

- `NoRelays`: nothing syncs the owner here. There is no snapshot yet, the
  owner's transports are still being set up, the app uses no relays for it,
  it is registered only as readonly, or the database refused startup, which
  `evoluError` reports.
- `Syncing`: a relay connects for the first time or reconciles. A first
  connection to a host that drops packets stays `Syncing` until the platform
  gives up on it, which can take a minute.
- `Synced`: a relay is up to date, and none syncs.
- `Offline`: every relay is disconnected. Evolu keeps reconnecting, up to 30
  seconds apart, so `Offline` can briefly outlast the outage.
- `Error`: a relay failed or offers a change this database skipped. `error` is
  the newest failure, or without one, the newest skipped change: a failure
  stops syncing through its relay, while a skipped change leaves out only
  that change.

Evolu stores changes in the local database before they sync, so sync needs no
UI while it works: show nothing for `NoRelays`, `Syncing`, and `Synced`. An
indicator that changes with every edit distracts, and screen readers announce
each change.

For `Offline` and `Error`, show one quiet line that lasts as long as the
status, not a dialog, which interrupts, or a toast, which disappears while
the problem lasts. Do not say that changes are saved on this device unless
[Evolu.devicePersistence](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#devicepersistence) is `Persisted`: a browser may keep them only
for a private session or delete them later. Evolu reports `Offline` at once;
an app may wait a few seconds before showing it, because brief
disconnections, such as waking from sleep, reconnect quickly. For `Error`,
write actionable text for the error types the app can act on, such as a
[ProtocolQuotaError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolQuotaError): the relay stores no more data for the owner, so
offer more quota, such as a plan upgrade, then call [Evolu.requestSync](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#requestsync)
with the owner's ID. For any other error, show generic text that names the
error type, which helps when the user reports it.

Render the line inside one element with `role="status"` that stays mounted:
screen readers announce changes only in a live region that already exists,
and a polite announcement fits a status that loses nothing. Do not use
`role="alert"`.

Show the app owner's status once for the whole app, such as below the header,
and another owner's status where the app shows that owner's data. Each Evolu
instance finds its own status by [Evolu.name](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#name), so an app with several
databases shows the status of the one the user works in.

### Example

```ts

// What to tell the user, or null while sync works or is not used.
const syncStatusToMessage = (status: OwnerSyncStatus): string | null => {
  switch (status.type) {
    case "NoRelays":
    case "Syncing":
    case "Synced":
      return null;
    case "Offline":
      return "Offline. Changes will sync when you're back online.";
    case "Error":
      return status.error.type === "ProtocolQuotaError"
        ? "Sync is paused because the sync server is full."
        : `Sync error: ${status.error.type}.`;
  }
};

assertEqual(syncStatusToMessage({ type: "Synced" }), null);
assertEqual(
  syncStatusToMessage({ type: "Offline" }),
  "Offline. Changes will sync when you're back online.",
);
assertEqual(
  syncStatusToMessage({
    type: "Error",
    error: {
      type: "ProtocolQuotaError",
      ownerId: testAppOwner.id,
      at: Millis.orThrow(1000),
    },
  }),
  "Sync is paused because the sync server is full.",
);
assertEqual(
  syncStatusToMessage({
    type: "Error",
    error: { type: "SyncFailed", at: Millis.orThrow(1000) },
  }),
  "Sync error: SyncFailed.",
);
```