Maybe I’m missing your point but it’s not that hard to detect and show when a content is fresh or cached.
Below is an example response you could give, showing how to add a custom header in your Service Worker (so the client knows when content is served from the cache), and how you might signal a “read-only” mode when offline.
One straightforward approach to indicate whether content is coming from the cache or the network is to add a custom header (e.g., X-Cache-Status
). That way, your frontend can easily detect if the response is cached and adjust the UI accordingly (e.g., disabling edits, showing a “read-only” theme, etc.).
Example: Adding a Custom Header in the Service Worker
If you’re using a custom fetch
event handler, you can do something like:
self.addEventListener('fetch', (event) => {
// Only intercept requests for todos, for instance
if (event.request.url.includes('/api/v1/todos')) {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
// Clone and add a custom header to indicate "HIT"
const responseWithHeader = new Response(cachedResponse.body, {
status: cachedResponse.status,
statusText: cachedResponse.statusText,
headers: cachedResponse.headers
});
responseWithHeader.headers.set('X-Cache-Status', 'HIT');
return responseWithHeader;
}
// Otherwise, fetch from the network
return fetch(event.request).then((networkResponse) => {
// Clone and add a custom header to indicate "MISS"
const responseWithHeader = networkResponse.clone();
responseWithHeader.headers.set('X-Cache-Status', 'MISS');
return responseWithHeader;
});
})
);
}
});
Then, on the client side, for example:
fetch('/api/v1/todos')
.then((response) => {
const cacheStatus = response.headers.get('X-Cache-Status');
if (cacheStatus === 'HIT') {
// We know this data is coming from the cache
// -> Maybe switch UI to "read-only" mode or show an offline badge
} else {
// It's coming from the network
// -> Normal behavior
}
return response.json();
})
.then((data) => {
// Render data in UI
});
Visual Indication of Offline/Read-Only
For the first step of offline functionality (caching only GET requests), you can:
- Disable all modifications in the UI (e.g., hide buttons or forms for adding/editing tasks) when you detect a
HIT
or a general “offline” state.
- Change the theme or style to signal “read-only” mode—this could be something like a greyed-out interface or a banner saying, “Offline: tasks are read-only.”
This way, users immediately know the data is not up to date and cannot make changes that will sync. If you later decide to implement background sync (for POST, PUT, PATCH, etc.), you can remove or adapt these UI restrictions and seamlessly handle offline edits in the background.