loading
Warning:
Chromium only, and it needs a service worker The whole API hangs off an active ServiceWorkerRegistration, so IsSupported is false until one is registered - even in a browser that implements it. Firefox and Safari do not. Register the demo worker below first.
Note:
Not the same as BackgroundSyncBackgroundSync gets your worker a few seconds of runtime when connectivity returns, and the work is yours to do. This transfers whole files for as long as it takes, the browser doing the transferring - and telling the user about it, in the same place it shows its own downloads.

Prerequisite: register the demo worker

ServiceWorker.Register

The same minimal /sw.js the rest of this category uses. Its backgroundfetchsuccess handler logs what arrived to the DevTools console.

@inject Bit.Butil.ServiceWorker serviceWorker

<button @onclick="RegisterWorker">Register /sw.js</button>

@code {
    private async Task RegisterWorker()
    {
        await serviceWorker.Register("/sw.js");

        // Background fetch hangs off the *active* registration, so a Fetch issued before the worker
        // has activated finds nothing to hang off.
        await serviceWorker.Ready();
    }
}
Live sample
support output
Results will appear here when you interact with the samples.

Start a fetch

Fetch

The id is your name for the transfer and has to be unique among the ones currently running. downloadTotal is what the browser's progress UI counts against - and a limit it enforces, so an estimate that is too low aborts the fetch. Pass 0 for an indeterminate UI.

C#
var registration = await backgroundFetch.Fetch(
    id: "manual-2024",
    urls: ["/media/chapter-1.pdf", "/media/chapter-2.pdf"],
    title: "Downloading the manual",
    downloadTotal: 12 * 1024 * 1024,
    icons: [new BackgroundFetchIcon { Src = "/icon-192.png", Sizes = "192x192", Type = "image/png" }]);

// null when the browser refused it - a duplicate id, or no support
Live sample
fetch output
Results will appear here when you interact with the samples.

Inspect and cancel

Get / GetIds / Abort

GetIds lists what is running for this registration - which is how a page that has just been reopened finds the transfer it started in an earlier session. A fetch disappears from here soon after it ends, so a null Get is 'not running now', not 'never existed'.

C#
string[] ids = await backgroundFetch.GetIds();

BackgroundFetchRegistrationInfo? info = await backgroundFetch.Get("manual-2024");
if (info is not null)
{
    var progress = info.DownloadTotal > 0
        ? (double)info.Downloaded / info.DownloadTotal
        : 0;
}

await backgroundFetch.Abort("manual-2024");
Live sample
inspect output
Results will appear here when you interact with the samples.

Watch the progress

SubscribeProgress

Progress events fire as bytes move, for as long as this page is open. They are a convenience, not the way to observe a transfer: the fetch outlives the page and the events do not - the service worker's backgroundfetchsuccess handler is what always runs.

C#
var subscription = await backgroundFetch.SubscribeProgress("manual-2024", info =>
{
    var done = info.Downloaded;
    var total = info.DownloadTotal;
    InvokeAsync(StateHasChanged);
});

// null when nothing is running under that id
await using var _ = subscription;
Live sample
progress output
Results will appear here when you interact with the samples.

Read what arrived

GetRecordUrls / ReadRecordText

A fetch is made of records - one request/response pair each. They can be read while the fetch is live; once it ends the browser releases them and RecordsAvailable goes false, which is why keeping the responses is the service worker's job.

C#
string[] recordUrls = await backgroundFetch.GetRecordUrls("manual-2024");

string? body = await backgroundFetch.ReadRecordText("manual-2024", recordUrls[0], timeoutMs: 5000);
Live sample
record output
Results will appear here when you interact with the samples.
Note:
Where the responses go The service worker receives backgroundfetchsuccess, backgroundfetchfail and backgroundfetchabort, and that handler is where the responses should be stored - in a Cache or in the origin private file system. By the time a page asks for them, they are usually gone.

API reference

Member
Signature
Description
IsSupported
ValueTask<bool> IsSupported()
True when the active service worker registration exposes backgroundFetch.
Fetch
ValueTask<BackgroundFetchRegistrationInfo?> Fetch(string id, string[] urls, string title = &quot;&quot;, long downloadTotal = 0, BackgroundFetchIcon[]? icons = null)
Starts a background fetch. Null when the browser refused it.
Get
ValueTask<BackgroundFetchRegistrationInfo?> Get(string id)
Reads a running fetch's progress, or null when nothing is running under that id.
GetIds
ValueTask<string[]> GetIds()
Lists the ids of the fetches currently running for this service worker registration.
Abort
ValueTask<bool> Abort(string id)
Cancels a running fetch. The worker gets a backgroundfetchabort event.
GetRecordUrls
ValueTask<string[]> GetRecordUrls(string id)
The request URLs a running fetch is made of, in order.
ReadRecordText
ValueTask<string?> ReadRecordText(string id, string url, int timeoutMs = 10000)
Reads one of a fetch's responses as text once that request has finished.
SubscribeProgress
Task<ButilSubscription?> SubscribeProgress(string id, Action<BackgroundFetchRegistrationInfo> handler)
Subscribes to a running fetch's progress events. Null when nothing is running under that id.
BackgroundFetchRegistrationInfo
class BackgroundFetchRegistrationInfo { string Id; long UploadTotal; long Uploaded; long DownloadTotal; long Downloaded; string Result; string FailureReason; bool RecordsAvailable; }
A fetch's counters, and how it ended if it has. Result is empty while it runs.
BackgroundFetchIcon
class BackgroundFetchIcon { string Src; string? Sizes; string? Type; string? Label; }
One icon for the browser's own download UI.
An unhandled error has occurred. Reload 🗙