Docs Guides

Offline Support

Use ManualRetryPlugin with RETRY_MODES.MANUAL to queue requests while offline and replay them transparently on reconnect.

Complete offline-first setup

import { createRetryer, RETRY_MODES } from 'axios-retryer';
import { createManualRetryPlugin } from 'axios-retryer/plugins/ManualRetryPlugin';
import { createTokenRefreshPlugin } from 'axios-retryer/plugins/TokenRefreshPlugin';

const manualRetry = createManualRetryPlugin({
  maxRequestsToStore: 100,
  manualRetryMaxAge: 10 * 60_000,    // Discard after 10 minutes
  storeNonIdempotent: false,          // Only safe idempotent requests
  storeAuthRequests: false,           // Don't store auth-bearing requests

  // Re-attach fresh auth before replay
  rehydrateAuth: (config) => {
    config.headers ??= {};
    config.headers['Authorization'] = `Bearer ${auth.getAccessToken()}`;
    return config;
  },
});

const retryer = createRetryer({ mode: RETRY_MODES.MANUAL })
  .use(manualRetry)
  .use(
    createTokenRefreshPlugin(async (axios) => {
      const { data } = await axios.post('/auth/refresh');
      return { token: data.accessToken };
    }),
  );

// React to network status
window.addEventListener('online', async () => {
  const pending = manualRetry.getStoredRequests();
  if (pending.length === 0) return;

  showBanner(`Syncing ${pending.length} pending changes...`);

  try {
    const results = await manualRetry.retryFailedRequests();
    showSuccess(`Synced ${results.length} changes`);
  } catch (err) {
    showError('Some requests failed to sync');
  }
});

window.addEventListener('offline', () => {
  showWarning('You are offline — changes will sync when reconnected');
});

Idempotency keys for safe mutation replay

Attach Idempotency-Key to mutations so they can be safely replayed without duplicate side effects:

async function submitOrder(order: Order) {
  const idempotencyKey = crypto.randomUUID();

  try {
    const { data } = await retryer.axiosInstance.post('/api/orders', order, {
      headers: { 'Idempotency-Key': idempotencyKey },
    });
    showSuccess('Order placed');
    return data;
  } catch (err) {
    showWarning('Offline — order queued for when you reconnect');
    // manualRetry stored this request including the Idempotency-Key header
    // so replay is safe even if the server already processed it
  }
}

Inspecting pending requests

// Show user a count of pending actions
function getPendingCount() {
  return manualRetry.getStoredRequests().length;
}

// Discard everything without replaying (e.g. user signs out)
function discardPending() {
  manualRetry.clearStoredRequests();
}

React integration example

// useNetworkSync.ts
import { useEffect, useState } from 'react';
import { manualRetry } from './api';

export function useNetworkSync() {
  const [syncing, setSyncing] = useState(false);

  useEffect(() => {
    async function syncOnline() {
      const pending = manualRetry.getStoredRequests().length;
      if (!pending) return;
      setSyncing(true);
      try {
        await manualRetry.retryFailedRequests();
      } finally {
        setSyncing(false);
      }
    }
    window.addEventListener('online', syncOnline);
    return () => window.removeEventListener('online', syncOnline);
  }, []);

  return { syncing };
}
Concurrency & Priority → ManualRetryPlugin docs