Workbox Examples: workbox-sw

For most developers, workbox-sw will be all your service worker needs to handle routing and caching.

workbox-sw handles both precaching (using a manifest generated by workbox-cli, workbox-webpack-plugin, or workbox-build), and runtime caching.

The example service worker for this page precaches a single file, and then sets up a number of runtime caching routes that will match requests made against various endpoints and for local files.

You can see the effect of the service worker by trying out the following:

to see how precached assets are served.

to see a RegExp route in action.

to demonstrate that requests which aren't matched by a route default to going against the network.

to see how a network-first strategey can have a maximum timeout, after which a previously cached response is used.

demonstrates cache expiration by fetching one of three sequential images, when the maximum cache size is two.


See more details by opening your browser's Developer Tools.

Explore the Code

JavaScript for Service Worker

 * Create an instance of WorkboxSW.
 * Setting clientsClaims to true tells our service worker to take control as
 * soon as it's activated.
const workboxSW = new WorkboxSW({clientsClaim: true});

 * precache() is passed a manifest of URLs and versions, and does the following
 * each time the service worker starts up:
 *   - Adds all new URLs to a cache.
 *   - Refreshes the previously cached response if the URL isn't new, but the
 *     revision changes. This will also trigger a Broadcast Channel API message
 *     sent to the channel 'precache-updates'.
 *   - Removes entries for URLs that used to be in the list, but aren't anymore.
 *   - Sets up a fetch handler to respond to any requests for URLs in this
 *     list using a cache-first strategy.
 * Instead, add one of our tools (workbox-cli, workbox-webpack-plugin, or
 * workbox-build) to your existing build process, and have that regenerate the
 * manifest at the end of every build.
  url: 'precached.txt',
  revision: '43011922c2aef5ed5ee3731b11d3c2cb',

 * registerNavigationRoute() is used for sites that follow the App Shell Model,
 * It tells the service worker that whenever there's a navigation request for
 * a new URL, instead of returning the HTML for that URL, return a previously
 * cached "shell" HTML file instead.
 * If you want more control over which navigations use the "shell" HTML, you
 * can provide an optional array of regular expressions:
 *   - whitelist (which defaults to [/./])
 *   - blacklist (which defaults to [])
 * (For the purposes of this demo, which doesn't follow the App Shell Model,
 * registerNavigationRoute() is commented out.)
// workboxSW.router.registerNavigationRoute('app-shell.html', {
//   whitelist: [/./],
//   blacklist: [],
// });

 * Requests for URLs that aren't precached can be handled by runtime caching.
 * Workbox has a flexible routing system, giving you control over which caching
 * strategies to use for which kind of requests.
 * registerRoute() takes a RegExp or a string as its first parameter.
 *   - RegExps can match any part of the request URL.
 *   - Strings are Express-style routes, parsed by
 * registerRoute() takes a caching strategy as its second parameter.
 * The built-in strategies are:
 *   - cacheFirst
 *   - cacheOnly
 *   - networkFirst
 *   - networkOnly
 *   - staleWhileRevalidate
 * Advice about which strategies to use for various assets can be found at
 * Each strategy can be configured with additional options, controlling the
 * name of the cache that's used, cache expiration policies, which response
 * codes are considered valid (useful when you want to cache opaque responses)
 * and whether updates to previously cached responses should trigger a message
 * using the BroadcastChannel API.
 * The following routes show this flexibility put to use.

 * Set up a route that will match any URL requested that ends in .txt.
 * Handle those requests using a network-first strategy.

 * Set up a route that will match any URL requested that starts with
 * Handle those requests using a network-first strategy, but with a timeout.
 * If there's no network response before the timeout, then return the previous
 * response from the cache instead.
  workboxSW.strategies.networkFirst({networkTimeoutSeconds: 3})

 * Set up a route that will match any URL requested that starts with
 * Handle those requests using a cache-first strategy, storing them in a
 * dedicated cache named 'images'.
 * That cache has a maximum size of 2 entries,
 * and once that's reached, the least-recently used entry will be deleted.
 * Additionally, any entries older than 7 * 24 * 60 * 60 seconds (1 week) will
 * be deleted.
 * Because the image responses are cross-domain and don't use CORS, they will
 * be "opaque", and have a status code of 0. When using a cache-first strategy,
 * we need to explicitly opt-in to caching responses with a status of 0.
    cacheName: 'images',
    cacheExpiration: {
      maxEntries: 2,
      maxAgeSeconds: 7 * 24 * 60 * 60,
    cacheableResponse: {statuses: [0, 200]},
JavaScript for this Page
const fetchAndLog = async (url) => {
  log('info', `Requesting ${url}...`);
  try {
    const response = await fetch(url);
    const text = await response.text();
    log('info', `...the response is '${text}'.`);
  } catch(error) {
    log('warn', `...fetch failed due to '${error}'.`);

 * Any precached assets that are updated will automatically generate a message
 * using the BroadcastChannel API. Our page can listen for this message and
 * find out what was updated.
const precacheUpdates = new BroadcastChannel('precache-updates');
precacheUpdates.addEventListener('message', (event) => {
  log('info', `${} was updated.
      The new value will be used the next time a request is made.`);

const httpBinImgElement = document.querySelector('#httpbinimage');
const httpBinImageFormats = ['jpeg', 'png', 'webp'];

const buttonHandlers = {
  precached: () => fetchAndLog('precached.txt'),
  hello: () => fetchAndLog('hello.txt'),
  notmatched: () => fetchAndLog('not-matched.dat'),
  delay: () => fetchAndLog(
    `${Math.floor(Math.random() * 5) + 1}`),
  image: () => {
    const nextImageFormat = httpBinImageFormats.shift(httpBinImageFormats);
    httpBinImgElement.src = `${nextImageFormat}`;

const buttons = document.querySelectorAll('button');
for (const button of [...buttons]) {
  button.addEventListener('click', buttonHandlers[]);