Works with any backend npm install Zero dependencies

Queue uploads while offline; auto-resume when the connection returns

offlineMode: true turns MultipleUpload into an offline-first queue. Pick files with no network, hit upload - the queue is persisted to IndexedDB (state + Blob contents), all transfers pause, and a connectionChange event fires. When the network comes back, uploads automatically resume from where they were. Pair with a registered Service Worker for background-sync even when the tab is closed. None of Uppy, FilePond, Dropzone, or FineUploader offer this built-in.

Connection: Online
Try it:
  1. Open DevTools -> Network tab -> set to Offline.
  2. Pick a file. Hit upload. Note the queue is paused and the pill turns red.
  3. Reload the page. The file is still there - pulled from IndexedDB.
  4. Toggle Network back to Online. The upload resumes from byte 0 (or the last persisted chunk for chunked / S3 / GCS / tus).
Configuration
new MultipleUpload('#uploader', {
 uploadUrl: '/api/upload',
 offlineMode: true // forces persistState + indexeddb + persistBlobs
});
What happens under the hood
  • Forced defaults: persistState: true, persistAdapter: 'indexeddb', persistBlobs: true are applied automatically. Without them the queue can't survive a reload.
  • Online/offline events: listens to window.addEventListener('online'/'offline') + the initial navigator.onLine.
  • Going offline: calls pauseAll() internally. New tasks added while offline land in paused state.
  • Coming online: calls resumeAll() + kicks the queue.
  • Reload while offline: the IndexedDB-persisted state + Blobs rehydrate the queue. Uploads stay paused until online fires.
Service Worker for background sync

Register a Service Worker to make uploads happen even with the tab closed:

// Once at app startup:
await MultipleUpload.registerServiceWorker('/upload-sw.js');

// Then enable offline mode on any uploader:
new MultipleUpload('#uploader', {
 uploadUrl: '/api/upload',
 offlineMode: true
});
Reference Service Worker
// /upload-sw.js
self.addEventListener('sync', async (event) => {
 if (event.tag === 'multipleupload-queue') {
 event.waitUntil(replayQueuedUploads());
 }
});

async function replayQueuedUploads() {
 const db = await openMultipleUploadDB();
 const queue = await db.getAll('pendingUploads');
 for (const item of queue) {
 try {
 await fetch(item.url, {
 method: 'POST', body: item.formData,
 headers: item.headers
 });
 await db.delete('pendingUploads', item.id);
 } catch (e) { /* will retry on next sync event */ }
 }
}
Use cases
  • Field-data collection: capture photos / docs in remote areas, sync back to base.
  • Flaky-network mobile: let users continue working in a tunnel / elevator.
  • Bulk uploads with intermittent WiFi: the queue just resumes - no UI babysitting.
  • Coffee-shop laptops: uploads survive captive-portal disconnects.