← Docs

Recipe: Offline indicator + retry UX

Gracefully surface connectivity loss and provide a one-click retry loop without blocking the user.

Ingredients

  • navigator.onLine + window events
  • Exponential backoff with jitter
  • Toast-style banner, non-blocking
  • Retry queue for failed mutations

Steps

  1. Subscribe to online / offline events on window.
  2. On offline, render a fixed bottom banner:
    <div className="fixed bottom-0 inset-x-0 bg-[#F472B6]/10
      border-t border-[#F472B6]/30 px-4 py-3 text-center">
      <span className="text-[#F472B6] text-sm">
        You are offline. Changes will sync when reconnected.
      </span>
    </div>
  3. Queue writes in a local array. On online, drain the queue with retry logic:
    async function drainQueue(queue, attempt = 0) {
      while (queue.length) {
        const item = queue[0];
        try {
          await send(item);
          queue.shift();
          attempt = 0;
        } catch {
          const delay = Math.min(1000 * 2 ** attempt + Math.random() * 1000, 30000);
          await new Promise(r => setTimeout(r, delay));
          attempt++;
        }
      }
    }
  4. Add a manual “Retry now” button that calls drainQueue immediately.
  5. Clear the banner on successful drain. Keep a small green confirmation for 3 seconds.

Edge cases

  • Flaky connections: debounce online/offline toggles by 500ms.
  • Tab hidden: pause retries when document.hidden is true.
  • Stale data: attach a monotonic version to each queued item; discard superseded writes.