# Choosing a caching strategy

> How to pick the right service worker caching strategy for your PWA — cache-first, network-first, stale-while-revalidate, and when to use each.

**In one line:** the right caching strategy depends on whether the resource is static,
dynamic, or time-sensitive — and how tolerant your users are of stale content.

## The core strategies

Service workers intercept `fetch` events and decide where to get the response: the cache,
the network, or both. Each strategy makes a different trade-off between speed, freshness,
and offline availability.

### Cache-first

Check the cache first; fall back to the network if nothing is cached.

```js
async function cacheFirst(request) {
  const cached = await caches.match(request);
  if (cached) return cached;
  return fetch(request);
}
```

**Use for:** static assets (JS bundles, CSS, images, fonts) that don't change between deploys.
Once cached, responses are instant — even offline.

### Network-first

Try the network first; fall back to the cache if the request fails or times out.

```js
async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open('dynamic');
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}
```

**Use for:** dynamic content (API responses, user data) where freshness matters but offline
fallback is still valuable.

### Stale-while-revalidate

Return the cached response immediately, then fetch a fresh copy in the background for
next time.

```js
async function staleWhileRevalidate(request) {
  const cached = await caches.match(request);
  const fetchPromise = fetch(request).then((response) => {
    const cache = caches.open('dynamic');
    cache.then((c) => c.put(request, response.clone()));
    return response;
  });
  return cached || fetchPromise;
}
```

**Use for:** semi-static content (UI chrome, article lists) where showing something quickly
is more important than showing the absolute latest version.

### Network-only

Always go to the network, never the cache.

**Use for:** non-GET requests (POST, PUT, DELETE), real-time data (WebSocket connections),
or resources that must never be stale.

### Cache-only

Serve exclusively from the cache; fail if the resource is not cached.

**Use for:** pre-cached resources during an install event (app shell, offline fallback page).

## Choosing a strategy

| Resource type | Recommended strategy | Why |
|---|---|---|
| App shell (HTML, CSS, JS) | Cache-first | Rarely changes; instant load after first visit |
| Images and fonts | Cache-first | Immutable between deploys when hashed filenames are used |
| API data (user-specific) | Network-first | Must be fresh; cache provides offline fallback |
| API data (shared/semi-static) | Stale-while-revalidate | Show cached data fast, update in background |
| Navigation requests | Network-first with offline fallback | Fresh HTML when online; cached shell when offline |

## Time-based expiration

For resources that are neither immutable nor critical, add a time check to stale-while-
revalidate or cache-first. Store the response timestamp alongside the cached response and
fall back to the network if the cache is older than your threshold:

```js
async function cacheWithExpiry(request, maxAgeSeconds) {
  const cached = await caches.match(request);
  if (cached) {
    const cachedDate = new Date(cached.headers.get('date'));
    if (Date.now() - cachedDate.getTime() < maxAgeSeconds * 1000) {
      return cached;
    }
  }
  return fetch(request);
}
```

## Practical checklist

- [ ] Use cache-first for hashed static assets (JS, CSS, images).
- [ ] Use network-first for API data and user-specific content.
- [ ] Use stale-while-revalidate for semi-static lists and UI chrome.
- [ ] Add a precache step during the `install` event for the app shell.
- [ ] Set a cache name that includes a version string so deploys bust old caches.
- [ ] Limit cache size and expiry to prevent unbounded storage growth.
- [ ] Always provide an offline fallback page for navigation requests.