· deepdives · 7 min read
Unlocking the Power of the Background Fetch API: A Step-by-Step Guide
Learn what the Background Fetch API is, how it works, and how to implement it in a Progressive Web App to handle long-running downloads/uploads in the background. Includes step-by-step code, real-world use cases, fallbacks, and testing tips.

Why the Background Fetch API?
Long-running network tasks - large file downloads, multi-image galleries, or big uploads - are painful to manage in single-page web apps: users close the page, navigate away, or lose connectivity and the task aborts. The Background Fetch API lets you delegate those tasks to the browser via a Service Worker so they can continue (and report progress) even after the user leaves your site.
Key benefits:
- Runs network tasks independently of the page lifecycle.
- Lets browser present a persistent UI for progress (platform-dependent).
- Handles retries and continuation across connectivity changes.
Note: Browser support is limited and evolving. See the compatibility section below.
How it works (high level)
- The page asks the Service Worker (via ServiceWorkerRegistration.backgroundFetch.fetch) to fetch one or more resources.
- The fetch is managed by the browser outside the page; progress and lifecycle live with the Service Worker.
- The Service Worker receives lifecycle events (success, failure, clicks) and can access the downloaded responses or store them (e.g., to IndexedDB or Cache API).
Browser support and limitations
- Background Fetch is a relatively new and experimental API with limited support in browsers. Check current compatibility before using in production.
- It requires a secure context (HTTPS) and a Service Worker.
- Not all platforms surface the same UI. Some browsers may not implement a persistent system-level UI; they still run the fetch logic but you’ll need to provide your own app UI for progress.
References:
- MDN: Background Fetch API - https://developer.mozilla.org/en-US/docs/Web/API/Background_Fetch_API
- WICG spec: https://wicg.github.io/background-fetch/
- Web.dev deep-dive: https://web.dev/background-fetch/
Before you begin: prerequisites
- Serve your site over HTTPS.
- Register a Service Worker with a scope that covers the pages that will request background fetches.
- Test on a browser that supports the API (check the links above).
Step-by-step implementation
Below is a practical example that shows how to:
- Register a Service Worker
- Trigger a background fetch from the page
- Handle background fetch events inside the Service Worker and save responses to the Cache
1) Register the Service Worker (in your main page)
if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('/sw.js')
.then(reg => console.log('SW registered', reg))
.catch(err => console.error('SW registration failed', err));
}Make sure /sw.js is under the scope you want for background fetches.
2) Trigger a Background Fetch from the page
From a user gesture (best practice), request the background fetch via the registration’s backgroundFetch manager:
async function startBackgroundDownload(urls) {
if (!('serviceWorker' in navigator))
throw new Error('No service worker support');
const reg = await navigator.serviceWorker.ready;
if (!reg.backgroundFetch) {
throw new Error('Background Fetch not supported in this browser');
}
try {
// id can be any string that identifies this fetch
const bgFetch = await reg.backgroundFetch.fetch('my-download-id', urls, {
title: 'Downloading assets',
icons: [
{
sizes: '192x192',
src: '/icons/download-icon.png',
type: 'image/png',
},
],
downloadTotal: 0, // optional: unknown total size
});
console.log('Background fetch started', bgFetch);
} catch (err) {
console.error('Background fetch failed to start', err);
}
}
// Example usage (must be called after a user gesture):
const assetUrls = [
'/large-video.mp4',
'/high-res-image-1.jpg',
'/high-res-image-2.jpg',
];
startBackgroundDownload(assetUrls);Notes:
urlscan be an array of strings or Request objects.- The options object can include
title,icons, anddownloadTotal.
3) Handle events in the Service Worker
In /sw.js, listen for Background Fetch events. The main lifecycle events are backgroundfetchsuccess, backgroundfetchfail, and backgroundfetchabort. On success, you typically want to obtain the responses and persist them (Cache API, IndexedDB, or file system API if available).
self.addEventListener('backgroundfetchsuccess', event => {
// Keep service worker alive until work completes
event.waitUntil(
(async () => {
const bgFetch = event.registration;
// Get the records for individual requests
const records = await bgFetch.matchAll();
// Open a cache to store downloaded responses
const cache = await caches.open('background-fetch-cache-v1');
for (const record of records) {
// `record` has a `responseReady` Promise that resolves with a Response
const response = await record.responseReady;
// Choose how you want to store the response. Example: Cache API
const request = record.request;
await cache.put(request, response);
}
// Optionally update the UI shown by the platform
try {
await bgFetch.updateUI({
title: 'Download complete',
});
} catch (err) {
// updateUI may not be supported in all browsers
console.warn('updateUI not supported', err);
}
// You can also show a notification
self.registration.showNotification('Downloads ready', {
body: 'Your background downloads are finished.',
icon: '/icons/notify-icon.png',
});
})()
);
});
self.addEventListener('backgroundfetchfail', event => {
event.waitUntil(
(async () => {
// Handle failure: inform the user, clean up partial data, retry logic, etc.
console.warn('Background fetch failed', event.registration.id);
self.registration.showNotification('Download failed', {
body: 'Some files failed to download in the background.',
});
})()
);
});
self.addEventListener('backgroundfetchabort', event => {
// User cancelled or the fetch was aborted
console.log('Background fetch was aborted', event.registration.id);
});
// Optional: handle a user click on the background fetch UI
self.addEventListener('backgroundfetchclick', event => {
event.waitUntil(
(async () => {
// Focus or open the relevant client page
const clientsList = await clients.matchAll({
type: 'window',
includeUncontrolled: true,
});
if (clientsList.length > 0) {
clientsList[0].focus();
} else {
clients.openWindow('/downloads');
}
})()
);
});Important implementation notes:
- Each
recordreturned bymatchAll()represents one request/response pair. - Access the response using
record.responseReady- a Promise that resolves to a Response. - Use
event.waitUntil()to keep the Service Worker alive while you process the data.
4) Reading cached items in your page
Later, in a page, you can read the cached responses from the Cache API or from whatever storage you used:
async function showDownloadedAssets() {
const cache = await caches.open('background-fetch-cache-v1');
const keys = await cache.keys();
for (const request of keys) {
const response = await cache.match(request);
// Do whatever you need with the response (create blob URLs, display images, etc.)
}
}Real-world scenarios and patterns
- Offline-first media downloads: let users queue podcasts, videos, or image galleries to download while they continue using other sites or lock their phone.
- Large file uploads: when uploading many/large files, use Background Fetch where supported so the upload continues if the user navigates away (server-side must support idempotent/resumable uploads).
- App update assets: download optional feature bundles, maps, or game assets in the background.
- E-commerce receipts: upload large receipts or images in the background after the purchase completes.
Handling unsupported browsers (fallbacks)
Because support is limited, implement graceful fallbacks:
- Detect support (check
serviceWorkerRegistration.backgroundFetch) and fall back to an in-page fetch with progress and a clear UX warning that the task will stop if the page is closed. - Use Background Sync (SyncManager) for smaller or resumable uploads when Background Fetch isn’t available.
- Implement chunked uploads with IndexedDB + periodic background sync or server-side resumable upload endpoints.
Example feature detect:
const swReg = await navigator.serviceWorker.ready;
if (swReg.backgroundFetch) {
// use Background Fetch
} else {
// fallback: start fetch and show progress in-page, or queue via IndexedDB
}Testing, debugging, and best practices
- Must be served over HTTPS.
- Trigger background fetches via a user gesture where possible to avoid blockers.
- Use realistic large files in testing so the background workflow is exercised.
- Use DevTools (Application tab) and the Service Worker pane to monitor registrations. Some browsers include a Background Fetch section in DevTools; support varies.
- Remember to clean up caches and data after use to avoid filling device storage.
- Be mindful of battery and data usage. Respect user preferences and offer clear opt-in UX for large background transfers.
Security and privacy considerations
- Only fetch from origins and endpoints you control or trust. Background fetch continues outside the page, so be careful with credentials and tokens.
- If using credentials, ensure proper CORS and
credentialshandling on the server side. - Provide transparent UI so users know large background transfers may consume data.
When not to use Background Fetch
- Small, ephemeral requests - Background Fetch adds complexity and isn’t needed for short operations.
- When you need precise control of partial progress at the page level and can keep the page open for the transfer.
Summary / Checklist
- Ensure HTTPS and a Service Worker are in place.
- Feature-detect
serviceWorkerRegistration.backgroundFetch. - Start background fetches from a user gesture.
- Handle lifecycle events in the Service Worker and persist downloaded responses as needed.
- Provide fallbacks for unsupported browsers and be mindful of UX for battery/data.
Background Fetch is a powerful tool for enabling long-running network tasks in PWAs and other web apps. When supported, it lets you deliver a better user experience by taking heavy work out of the foreground page and letting the browser manage the lifecycle.
References
- MDN: Background Fetch API - https://developer.mozilla.org/en-US/docs/Web/API/Background_Fetch_API
- WICG spec: https://wicg.github.io/background-fetch/
- web.dev: Background Fetch - https://web.dev/background-fetch/



