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
- Subscribe to
online/offlineevents onwindow. - 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> - 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++; } } } - Add a manual “Retry now” button that calls
drainQueueimmediately. - 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.hiddenis true. - Stale data: attach a monotonic version to each queued item; discard superseded writes.