Offline App Shell
Start by caching only the shell needed to show a useful page. Add runtime caching after you understand your data freshness rules.
1. Add An Offline Page
<main>
<h1>You are offline</h1>
<p>Open the app again when your connection returns. Saved drafts remain on this device.</p>
</main>
2. Cache Shell Assets
const CACHE_NAME = 'shell-v1';
const SHELL_ASSETS = ['/', '/offline.html', '/styles.css', '/src/main.js'];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(SHELL_ASSETS))
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key)))
)
);
});
3. Return Fallback For Navigation
self.addEventListener('fetch', (event) => {
if (event.request.mode !== 'navigate') return;
event.respondWith(
fetch(event.request).catch(async () => {
const cache = await caches.open(CACHE_NAME);
return cache.match('/offline.html');
})
);
});
Gotcha: This example assumes stable asset URLs. Real bundlers usually fingerprint JS and CSS. Use a build-integrated service worker or Workbox when asset lists are generated.