Precaching strategies: shipping the app shell ahead of time
Published
In one line: precaching stores a known, fixed set of files into the cache during the service worker’s install step, so the app shell is on the device before it is needed — the first navigation after install can render instantly and offline. It is the counterpart to runtime caching, which stores responses as the user requests them.
Precache vs runtime cache
Section titled “Precache vs runtime cache”- Precache — a fixed list known at build time (HTML shell, CSS, JS, icons, fonts). Fetched
and stored during
install, before the page asks for them. Suited to the app shell and other critical, always-needed assets. - Runtime cache — populated in the
fetchhandler as requests happen, under a chosen caching strategy. Suited to content that varies or is too large/numerous to list up front (API responses, user images, articles).
Precaching and runtime caching are complementary: precache the shell so the first load is fast and offline-capable, and runtime-cache the dynamic content around it.
What to precache (and what not to)
Section titled “What to precache (and what not to)”- Precache: the minimal app shell — the HTML/CSS/JS and icons needed to render a usable
first screen. Everything listed here is downloaded and stored during the
installevent. - Runtime-cache instead: content that varies, is large, or is requested on demand is typically handled with a runtime caching strategy rather than being precached.
Precache the skeleton; let a runtime strategy handle the rest.
Why content revisioning is the core problem
Section titled “Why content revisioning is the core problem”Precaching is only safe if the service worker can tell when a precached file has changed.
If you precache /app.js and later deploy a new /app.js at the same URL, the service worker
needs a signal that the content differs, or users keep the stale file.
Two ways to make change detectable:
- Versioned URLs (
/app.abc123.js): the URL itself changes when content changes, so the precache entry is naturally new. Workbox treats a URL that already carries versioning information (such as a content hash) as its own revision. - A revision per entry: pair each unversioned URL (like
/index.html) with a content revision, so the cache refetches when the revision differs even though the URL is the same.
This bookkeeping is what a build-time precache manifest automates — maintaining it by hand is error-prone, which is why precaching is usually delegated to a library.
Workbox precaching in practice
Section titled “Workbox precaching in practice”Workbox’s workbox-precaching module consumes a precache manifest (a list of
{ url, revision } entries, generated at build time) and handles the lifecycle for you:
- During
install, it fetches and stores every listed asset. - During
activate, it removes entries from previous precaches that are no longer in the manifest, so old shell versions are cleaned up automatically. - It serves precached responses from the cache, and refetches an entry when its revision changes.
import { precacheAndRoute } from 'workbox-precaching';
// self.__WB_MANIFEST is injected at build time with { url, revision } entries.precacheAndRoute(self.__WB_MANIFEST);Workbox provides build tooling — workbox-build, the workbox-webpack-plugin, and the
workbox-cli — to generate that precache manifest from your actual build output, so the
list and revisions stay in sync with every deploy.
Relationship to the HTTP cache
Section titled “Relationship to the HTTP cache”Precaching lives in the Cache Storage layer and is separate from the browser’s HTTP cache. The two can interact: a precache fetch may itself be served from the HTTP cache, which can land a stale copy in your precache. Where that risk matters, use versioned URLs so each version is a distinct request. Keep both layers’ lifetimes in mind so they don’t work against each other.
Practical checklist
Section titled “Practical checklist”- Precache only the minimal app shell; runtime-cache dynamic and large content.
- Prefer versioned (hashed) URLs so changed assets are distinct requests.
- For unversioned URLs (like
/index.html), pair them with a content revision. - Generate the precache manifest at build time with Workbox tooling; don’t hand-maintain the list.
- Rely on Workbox to clean up superseded precaches on
activate. - Watch for HTTP-cache interactions that could precache a stale asset.