Cache API in JavaScript Service Workers for Offline Use
The Cache API provides a persistent storage mechanism in modern browsers that allows service workers to intercept network traffic and save Request/Response object pairs. By decoupling resource delivery from an active internet connection, developers can store critical assets such as HTML, CSS, JavaScript files, and API data directly on the client’s device, enabling web applications to load instantly and function completely offline.
The Request and Response Storage Model
Unlike traditional HTTP caching mechanisms governed by HTTP headers,
the Cache API is fully programmable via JavaScript. It acts as a
key-value store where the key is a Request object (or a URL
string) and the value is a Response object.
Because the storage is tied to the origin of the web application, service workers operate inside an isolated sandbox, ensuring cached network assets are securely segregated per domain.
Opening and Creating Caches
To store data, a service worker first opens a named cache using the
global caches.open() method. If the specified cache does
not exist, the browser automatically creates it.
const CACHE_NAME = 'app-shell-v1';
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
'/',
'/index.html',
'/styles/main.css',
'/scripts/app.js'
]);
})
);
});The cache.addAll() method accepts an array of URLs,
fetches them across the network, and automatically saves the resulting
Response objects into the cache upon completion.
Dynamic Caching with
cache.put()
When handling runtime traffic, service workers intercept fetch events
and store responses dynamically using the cache.put()
method.
Because Response bodies are readable streams that can
only be read once, the response must be cloned using
response.clone() before saving it to the cache if it is
also being returned to the browser.
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request).then((networkResponse) => {
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseToCache);
});
return networkResponse;
});
})
);
});Retrieving Cached Responses Offline
When a user triggers a network request, the service worker’s
fetch event listener intercepts it. The
caches.match() method evaluates whether the incoming
event.request matches an existing entry in the cache.
If a match is found, caches.match() resolves with the
cached Response object without making a network call. If no
match is found, the request falls back to the network using
fetch(). If the user is offline and the asset has been
cached, the application serves the saved response seamlessly.
Cache Maintenance and Cleanup
Because cached data persists indefinitely until manually cleared or
purged by the browser under extreme storage pressure, service workers
use the activate event to remove outdated caches. By
comparing existing cache keys against the current version, obsolete
assets are deleted using caches.delete(), ensuring the user
does not consume stale resources.