Skip to content
txbrendan/ui

Sync Status

beta

Where a save stands, beside what changed: saving, saved, waiting for a signal, or failed, with a retry icon.

Preview

saving

Saving…

saved

SavedSaved at 18:04

offline

Offline · 3 changes will send when you're back online

failed

Couldn't save · No signal

size

Offline · 1 change will send when you're back onlineOffline · 1 change will send when you're back onlineOffline · 1 change will send when you're back online

sm

Weekly reportCouldn't save · No signal

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/sync-status

Use it for

  • Beside what changed: a card header, next to a form's buttons, under a total that just changed.
  • Put it in a field's status slot, like FieldFrame's or an editor's, with size sm. It sits on the label line, 16px tall.
  • offline counts what waits to send: Offline · 3 changes will send when you're back online. Apply changes on screen at once anyway. This line is the promise they'll send.
  • failed says why in plain words with reason, like No signal, and onRetry ends the line with a retry icon.
  • saved is quiet. Pass at, like 18:04, to say when.

Avoid it for

  • A toast for a save. The status sits beside what changed.
  • A status on a row or in a table. Use StatusText.
  • Saying Saved after every keystroke. Show saving while a save runs, then saved.

Props

  • state

    Required. Where the save stands.

    "saving" | "saved" | "offline" | "failed"

  • count

    offline: how many changes wait to send.

    number

  • at

    saved: when, like "18:04".

    string

  • reason

    failed: why, in plain words, like "No signal".

    string

  • onRetry

    failed: a retry icon ends the line.

    () => void

  • retryLabel

    Names the retry icon, in its tooltip and to screen readers.

    string · default "Try again"

  • size

    sm sits on a field's label line: meta type, 16px tall. md is body type, and lg is section type.

    "sm" | "md" | "lg" · default "md"

  • className

    Extra classes on the line. Later classes win.

    string

Accessibility

  • The words sit in a polite status region, so a change is said once, without taking focus.
  • The state is said in words: Saving, Saved, Offline, Couldn't save. Color only marks it.
  • The retry icon is a button named by retryLabel, which also shows in its tooltip. It sits outside the status region, so its name is not said with every change.
  • The icons are hidden from screen readers, since the words say the same.

Examples

Saving

<SyncStatus state="saving" />

Offline

<SyncStatus state="offline" count={3} />

Failed, with a retry

<SyncStatus state="failed" reason="No signal" onRetry={() => save()} />