Periodic Background Sync
Periodic Background Sync
Section titled “Periodic Background Sync”Periodic Background Sync (PBS) allows an installed PWA to wake up its service worker on a recurring schedule and refresh data — even when the user is not actively using the app. When the user next opens the app, they see fresh content instead of stale data.
Think of it as a cron job that runs inside the browser, subject to the browser’s own heuristics about when it is safe and beneficial to wake up your app.
How it works
Section titled “How it works”sequenceDiagram
participant App as Installed PWA
participant SW as Service Worker
participant Browser as Browser Scheduler
participant Net as Network / API
App->>SW: registration.periodicSync.register('refresh-news', { minInterval })
Browser-->>SW: periodicsync event (browser decides timing)
SW->>Net: fetch('/api/latest-articles')
Net-->>SW: JSON response
SW->>SW: caches.open('news').put(...)
Note over App: User opens app — sees fresh content Checking for support and permission
Section titled “Checking for support and permission”async function checkPeriodicSyncSupport() { if (!('periodicSync' in (await navigator.serviceWorker.ready))) { console.warn('Periodic Background Sync is not supported'); return false; }
const status = await navigator.permissions.query({ name: 'periodic-background-sync' }); console.log('Periodic sync permission:', status.state); // "granted", "denied", or "prompt" return status.state === 'granted';}Registering a periodic sync
Section titled “Registering a periodic sync”async function registerPeriodicSync() { const registration = await navigator.serviceWorker.ready;
if (!('periodicSync' in registration)) return;
try { await registration.periodicSync.register('refresh-news', { minInterval: 24 * 60 * 60 * 1000, // 24 hours in milliseconds }); console.log('Periodic sync registered'); } catch (err) { console.warn('Periodic sync registration failed:', err); }}minInterval is a hint — the browser will fire the event no more often than this interval, but may fire it less often or not at all depending on its heuristics.
Handling the periodicsync event in the service worker
Section titled “Handling the periodicsync event in the service worker”// sw.js — service workerself.addEventListener('periodicsync', (event) => { if (event.tag === 'refresh-news') { event.waitUntil(refreshNewsCache()); }});
async function refreshNewsCache() { const response = await fetch('/api/latest-articles'); if (!response.ok) return;
const cache = await caches.open('news-v1'); await cache.put('/api/latest-articles', response); console.log('News cache refreshed at', new Date().toISOString());}event.waitUntil() keeps the SW alive until the async work completes. If your fetch takes too long or fails, the browser may not retry immediately.
Listing and unregistering syncs
Section titled “Listing and unregistering syncs”// List all registered periodic syncsconst registration = await navigator.serviceWorker.ready;const tags = await registration.periodicSync.getTags();console.log('Registered syncs:', tags);
// Unregister a specific syncawait registration.periodicSync.unregister('refresh-news');Limitations and availability
Section titled “Limitations and availability”| Constraint | Detail |
|---|---|
| Installed PWA only | Periodic sync requires the app to be installed to the home screen or desktop |
| Chromium only | As of 2025 only Chromium-based browsers support PBS; Firefox and Safari do not |
| Browser cadence | The browser decides the actual firing interval based on battery, network, and site engagement — treat minInterval as a minimum, not a schedule |
| Network required | The SW only runs the handler when the device has network connectivity |
| Engagement score | Chrome tracks site engagement; low-engagement sites may have their PBS throttled or disabled |
| HTTPS required | Like all service worker features, PBS only works on secure origins |
Best-effort design
Section titled “Best-effort design”Because the browser controls the cadence, design your app to work correctly even if periodicsync never fires:
- Always show cached data if the cache exists — even stale data is better than a blank screen.
- Refresh on page load as a fallback when the periodic sync did not run.
- Show a “last updated” timestamp so users understand the data freshness.
// main.js — page code (fallback refresh on open)async function loadArticles() { const cache = await caches.open('news-v1'); const cached = await cache.match('/api/latest-articles');
if (cached) { renderArticles(await cached.json()); // Still refresh in the background fetch('/api/latest-articles').then(async (res) => { if (res.ok) { await cache.put('/api/latest-articles', res.clone()); renderArticles(await res.json()); } }); } else { const res = await fetch('/api/latest-articles'); const data = await res.json(); renderArticles(data); }}Code-only lesson: Periodic Background Sync requires an installed PWA and is only available in Chromium-based browsers. The examples above are complete and accurate but cannot run in the in-browser playground. Test them in Chrome or Edge with your PWA installed.