Japp
How a PWA's offline mode works under the hood

How a PWA's offline mode works under the hood

A while back I talked about what a PWA is. Now I dig into the detail of what makes it keep working without a connection.

A while back I wrote about what a PWA is and why it’s worth it. What I didn’t cover then is the piece that makes the part that most impresses a client possible when I show it to them: that the app keeps opening even with the phone in airplane mode. That piece is the service worker, and under the hood it works quite differently from how I imagined before diving in properly.

It’s not magic, it’s a proxy you program yourself

A service worker is a script the browser runs separately from the page, in its own thread, and that can intercept every network request your app makes before it reaches the internet. The first time it registers, it doesn’t control anything yet — you have to reload the page once for it to start intercepting requests. That detail cost me a good while of debugging the first time: I’d test changes, nothing would happen, and the problem wasn’t my code but that I was still seeing the version without an active service worker.

The caching strategies I actually use

There’s no single way to decide what to return when a request is intercepted — it depends on what’s being requested:

For the app shell (HTML, CSS, JS of the app itself), I use cache-first: if it’s already cached, it’s served directly without even attempting the network, and the cache is only updated in the background. That’s what makes the app open instantly, even on a good connection.

For data that changes often, I use network-first with a cache fallback: try the network first, and if it fails (no connection), serve the last saved version. That way the user sees somewhat outdated data instead of a blank screen, which is almost always preferable.

What I learned the hard way

Caching everything without any criteria is as bad as caching nothing. The first version I built cached absolutely every request, including API calls with data that changed every minute — the result was an app showing old information as if it were current, with no warning at all. Since then I decide the strategy per resource type, never “cache everything” as a blanket rule. Genuinely useful offline mode isn’t the one that hides that the data is outdated, it’s the one that keeps working and is honest about how recent what you’re seeing actually is.

Leave without saving?

You have unsaved changes in this form. If you leave now, they'll be lost.

esen