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 };
}