Service Worker Activate Event — Cleaning Up Old Caches
In this tutorial, you will learn about Service Worker Activate Event. We cover key concepts, practical examples, and best practices to help you master this topic.
The activate event cleans up outdated caches and claims control of pages, ensuring your PWA uses fresh resources and discards stale data from previous versions after a service worker update.
What You'll Learn
By the end of this tutorial, you will understand how the activate event works, how to clean old caches safely, how to claim clients, and how to handle data Migration between versions.
Why It Matters
Without proper activation handling, old caches accumulate indefinitely on user devices. Users can end up with hundreds of megabytes of stale data from previous versions of your PWA. The activate event is your cleanup crew.
Real-World Use
An e-commerce PWA updated weekly with new product images. Without activate cache cleanup, users accumulated 150MB of old product images. Adding cache pruning during activation reduced storage to under 20MB per user.
Activate Event Flow
Activate Event Flow
New service worker installed
↓
Previous worker releases control
↓
Activate event fires
↓
List all cache names
↓
╔════════════════════════════════════╗
║ Compare each against current cache ║
╚════════════════════════════════════╝
↓
┌─────────────────┐
│ Old cache found │ → Delete it
└─────────────────┘
↓
Claim all clients (pages)
↓
New worker controls all pages
Think of the activate event like moving to a new apartment. The install event packed your boxes (cached resources). The activate event is when you throw away old items you no longer need (old caches) and tell everyone your new address (claim clients).
Basic Activate Handler
const CACHE_NAME = 'my-pwa-v2';
self.addEventListener('activate', event => {
console.log('Activate: Starting cleanup');
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheName !== CACHE_NAME) {
console.log(`Activate: Deleting ${cacheName}`);
return caches.delete(cacheName);
}
})
);
}).then(() => {
console.log('Activate: Cleanup complete, claiming clients');
return self.clients.claim();
})
);
});
Output:
Activate: Starting cleanup
Activate: Deleting my-pwa-v1
Activate: Deleting my-pwa-v0
Activate: Cleanup complete, claiming clients
Sophisticated Cache Pruning
For complex scenarios, you might want to keep multiple cache versions or implement a white-list approach:
const CURRENT_CACHES = {
static: 'static-v3',
dynamic: 'dynamic-v2',
images: 'images-v1'
};
self.addEventListener('activate', event => {
const expectedCaches = Object.values(CURRENT_CACHES);
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
// Check if this cache name is one we expect
if (!expectedCaches.includes(cacheName)) {
console.log(`Deleting unexpected cache: ${cacheName}`);
return caches.delete(cacheName);
}
})
);
}).then(() => {
console.log('All caches validated, claiming clients');
return self.clients.claim();
})
);
});
This approach explicitly lists the caches your current version expects. Any cache not in the list is deleted. This is safer than comparing against a single name because it handles multiple cache types.
Claiming Clients
self.clients.claim() takes control of all uncontrolled pages. Without it, pages that loaded before activation continue using the old service worker or no service worker at all.
// Understanding clients.claim()
self.addEventListener('activate', event => {
event.waitUntil(
// Claim all pages immediately
self.clients.claim().then(() => {
console.log('Now controlling all open pages');
// Optionally notify pages about the update
return self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({
type: 'SW_UPDATED',
version: CACHE_NAME
});
});
});
})
);
});
// In the page JavaScript, listen for messages
navigator.serviceWorker.addEventListener('message', event => {
if (event.data.type === 'SW_UPDATED') {
console.log('Service worker updated to:', event.data.version);
}
});
Output:
Now controlling all open pages
Service worker updated to: my-pwa-v2
Handling Data Migration
When your cache schema changes between versions, the activate event is the right place to migrate data:
// Migration example: restructured cache layout
const OLD_CACHE = 'my-pwa-v2';
const NEW_CACHE = 'my-pwa-v3';
self.addEventListener('activate', event => {
event.waitUntil(
// Open old cache and migrate entries
caches.open(OLD_CACHE).then(oldCache => {
return oldCache.keys().then(requests => {
return caches.open(NEW_CACHE).then(newCache => {
const migrationPromises = requests.map(request => {
return oldCache.match(request).then(response => {
// Optionally transform response before storing
if (response && response.ok) {
return newCache.put(request, response);
}
});
});
return Promise.all(migrationPromises);
});
});
}).then(() => {
// Delete old cache after migration
return caches.delete(OLD_CACHE);
}).then(() => {
return self.clients.claim();
})
);
});
Cleanup Verification
After activation, you can verify cleanup worked:
// In DevTools console
caches.keys().then(keys => {
console.log('Current caches:', keys);
keys.forEach(key => {
console.log(` - ${key}`);
});
});
// Should only show active caches, old ones should be gone
Output:
Current caches:
- static-v3
- dynamic-v2
- images-v1
Common Mistakes
- Not deleting old caches. Caches persist until explicitly deleted. Unused caches waste storage and may serve stale content if a cache name matches by accident.
- Calling clients.claim() before cleanup. Claim clients after cache cleanup to ensure pages get the clean cache state immediately.
- Hardcoding cache names. Use a cache name Strategy (prefix + version) rather than hardcoding. This makes automated cleanup easier.
- Forgetting to handle unexpected caches. Other scripts or browser extensions may create caches. Use an allowlist approach rather than deleting everything except your known cache.
- Not waiting for cleanup promises. The activate event terminates if you do not use event.waitUntil(). Always wrap async operations in waitUntil.
Practice Questions
- What is the primary purpose of the activate event?
- Why should you call clients.claim() during activation?
- How do you safely delete old caches without accidentally deleting caches from other applications?
- What happens if you do not use event.waitUntil() in the activate handler?
- When would you need to migrate data between cache versions?
Challenge: Write an activate handler that manages three cache types (static, dynamic, images). Use a white-list approach where only the current version of each cache type is kept. Delete all other caches. Verify by creating test caches and checking they are removed.
FAQ
Mini Project
Create a service worker with three cache versions: static-v1, images-v1, dynamic-v1. Write an activate handler that deletes only caches not in your current white-list. Test by programmatically creating old caches (caches.open('old-cache')) and verifying they are deleted on activation. Log all cache operations.
What's Next
Your service worker lifecycle is complete. Now learn how the fetch event intercepts network requests and implements Caching strategies.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro