Skip to content

Notifications · Concept

Periodic background sync for content updates

Published Updated

Limited availabilityNot supported in Firefox (Desktop), Firefox (Android), Safari (macOS), Safari (iOS)WICG draft

Periodic background sync lets an installed PWA refresh its cached content on a schedule the browser chooses, by firing a periodicsync event in the service worker while no page is open. It exists so that a news or weather app opens with current data instead of a spinner, and it is Chromium-only, gated on installation and on how often the user actually opens the app.

Flow diagram of periodic background sync: an installed PWA with enough site engagement checks the periodic-background-sync permission and calls periodicSync.register() with a tag and minInterval; the browser schedules the sync no more often than minInterval, fires the periodicsync event in the service worker when the time arrives and the device is online, and the worker fetches and caches new data inside event.waitUntil(). The cycle repeats until the app is uninstalled, engagement decays or periodicSync.unregister() is called.
Periodic background sync: from permission and registration to the repeating periodicsync event.

The page registers a tag with registration.periodicSync.register(tag, { minInterval }); the browser then fires periodicsync in the service worker no more often than minInterval, when the device is online, and only for as long as it judges the app worth waking. The API surface (PeriodicSyncManager, register(), getTags(), unregister(), the periodicsync event) is in Chrome 80 and Edge 80 and nowhere else (BCD api.PeriodicSyncManager); the method-level reference is the service worker entry.

Chrome fires the event only for a web app the user has installed and launched as a standalone app, not for a site in a tab, and only when the device is on a network it has used before (Periodic background sync, developer.chrome.com). The frequency is tied to the site engagement score Chrome keeps per origin, visible at about://site-engagement/: a score of zero means no events at all, and a higher score allows syncs closer to minInterval. If the user stops opening the app, the score decays and the events stop. minInterval is therefore a floor rather than a schedule, and a registration with minInterval: 0 leaves the cadence entirely to Chrome.

The feature is gated by the periodic-background-sync permission, which Chrome grants automatically to installed apps without a prompt. navigator.permissions.query({ name: 'periodic-background-sync' }) reports granted or denied from a window or a service worker, and register() rejects with NotAllowedError when it is denied, InvalidStateError when the registration has no active worker, and InvalidAccessError when called from a window that is not a top-level browsing context (PeriodicSyncManager: register(), developer.mozilla.org).

Background sync, despite the similar name, retries a failed request once connectivity returns; periodic background sync pulls fresh data on a cadence. Web Push also wakes the worker but interrupts the user with a notification and must do so under userVisibleOnly. Periodic sync is the only quiet, repeating option, which makes it right for a daily article cache and wrong for anything that must happen at a particular time; for that there is no shipped API, as the notification triggers entry explains.

Both examples keep the page-load refresh as the fallback and let the API remove it where it works.

Registering a daily article refresh when the app can

Section titled “Registering a daily article refresh when the app can”

Register after the service worker is active and the permission is granted. The catch covers the tab case in Chrome and every other engine, where the code falls back to refreshing on load.

async function enableDailyRefresh() {
const registration = await navigator.serviceWorker.ready;
if (!('periodicSync' in registration)) {
return refreshOnLoad(); // Firefox, Safari, or an uninstalled Chrome tab
}
const status = await navigator.permissions.query({ name: 'periodic-background-sync' });
if (status.state !== 'granted') return refreshOnLoad();
try {
await registration.periodicSync.register('update-articles', { minInterval: 24 * 60 * 60 * 1000 });
} catch {
refreshOnLoad(); // permission denied or no active worker: behave as before
}
}

refreshOnLoad() is the pre-existing behaviour, so an engine without the API loses nothing; the API removes the spinner where it is available.

Handling the event and skipping the load-time refresh

Section titled “Handling the event and skipping the load-time refresh”

The worker caches the response inside event.waitUntil(); the page checks getTags() and skips its own refresh when the sync is registered, because the cache is already fresh.

// service worker
self.addEventListener('periodicsync', (event) => {
if (event.tag === 'update-articles') {
event.waitUntil(caches.open('articles').then((cache) => cache.add('/api/articles')));
}
});
// page
async function maybeRefresh(registration) {
const tags = 'periodicSync' in registration ? await registration.periodicSync.getTags() : [];
if (!tags.includes('update-articles')) refreshOnLoad();
}

Keep the handler small: Chrome gives the worker a limited window, and a sync that downloads megabytes on a metered connection is the behaviour that lowers the engagement budget.

Specifications

SpecificationStatus
Periodic Background SyncWICG draft
  • Legend
  • Yes
  • Partial
  • Flag
  • No
  • Unknown
Browser / PlatformSupportVersionsConfidenceSourceNotes
Chrome (Desktop)Yes80highsource—
Chrome (Android)Yes80highsource1
Edge (Desktop)Yes80highsource2
Firefox (Desktop)No—highsource3
Firefox (Android)No—highsource45
Safari (macOS)No—highsource6
Safari (iOS)No—highsource78
Samsung InternetYes13.0highsource9
WebView (Android)No—highsource10
  1. Derived by browser-compat-data mirroring from Chrome.
  2. Derived by browser-compat-data mirroring from Chrome.
  3. No Firefox support is recorded in browser-compat-data.
  4. No Firefox for Android support is recorded in browser-compat-data.
  5. Derived by browser-compat-data mirroring from Firefox.
  6. No Safari support is recorded in browser-compat-data.
  7. No Safari on iOS support is recorded in browser-compat-data.
  8. Derived by browser-compat-data mirroring from Safari.
  9. Derived by browser-compat-data mirroring from Chrome Android.
  10. Implementation tracking: https://crbug.com/40151529.

Source data: /compatibility/periodic-background-sync.json · Global usage: 72 % (StatCounter 2026-05)

Source: spec · MDN · Last verified 2026-10-03 · Confidence: high (computed from sources)