Offline Banner

Counts changes queued while offline and stays up after reconnecting until they have actually synced, so nobody assumes their edits reached the server.

StopsEdits made offline never reached the server

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/offline-banner.json

The files land in your project. They're yours to edit.

Usage

offline-banner-usage.tsx
import { OfflineBanner } from "@/components/ui/offline-banner"

<OfflineBanner online={online} queued={unsynced.length} guardUnload={false} />

What it catches

  • Edits made offline that haven't synced
  • Reconnecting before the queue is flushed: banner stays until it is
  • Sync failure, with Retry
  • Leaving the page with unsynced changes (guardUnload)

Props

OfflineBanner props
PropTypeDefault
queued

Actions waiting to reach the server (from your outbox or mutation queue).

number0
online

Override detection, e.g. from your own health check. Omit to follow the browser.

boolean—
syncFailed

Syncing the queue failed after reconnecting. Shows a Retry action.

booleanfalse
onRetry

Retry syncing the queue.

() => void—
hideAfter

How long "All changes synced" stays before the banner hides, in ms.

number2500
guardUnload

Warn before closing the tab while offline with queued actions that would be lost.

booleantrue
formatCount

"3 changes". Override to name your items or translate.

(count: number) => stringdefaultCount
offlineText

Message templates; `{count}` is replaced by `formatCount(queued)`.

string"You're offline · {count} will sync when you reconnect"
offlineEmptyText

Shown offline when nothing is queued.

string"You're offline · changes will sync when you reconnect"
syncingText

Shown while queued changes sync.

string"Back online · syncing {count}"
syncedText

Shown once everything has synced.

string"Back online · all changes synced"
failedText

Shown when the sync fails.

string"Couldn't sync {count}"
retryLabel

Retry button text.

string"Retry"

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Status text uses role=status
  • Hidden banner is inert so it can't take focus
  • Retry is a button; the syncing spinner icon is aria-hidden

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Make queued writes idempotent so replays after reconnecting don't apply twice.

  • Only shows the queue state you pass; it doesn't queue or sync anything itself
  • navigator.onLine can report online on a network with no internet

What it stops

Edits made offline never reached the server

Also known as

offline · no internet · network status · sync queue · pwa · reconnecting · retry · connection lost

Dependencies

Pairs well with