Blog>
Snippets

Offline-first Strategy for Progressive Web Apps

A robust example of how to design an offline-first experience in a PWA, ensuring core functionalities are accessible without a network connection.
// Register the service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/service-worker.js').then(function(registration) {
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }, function(err) {
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}
This code checks if the browser supports service workers and registers the service worker script. The service worker is the key to enabling offline experiences in a PWA.
// The service-worker.js
self.addEventListener('install', function(event) {
  // Perform install steps
  var CACHE_NAME = 'my-site-cache-v1';
  var urlsToCache = [
    '/',
    '/styles/main.css',
    '/script/main.js'
  ];

  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(function(cache) {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});
Inside the service-worker.js file, this code handles the 'install' event, which occurs when the service worker is being installed. It defines a cache name and an array of URLs to cache, which allows the app to function offline by serving these files from the cache.
// The service-worker.js
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request)
      .then(function(response) {
        // Cache hit - return response
        if (response) {
          return response;
        }

        // IMPORTANT: Clone the request. A request is a stream and
        // can only be consumed once. Since we are consuming this
        // once by cache and once by the browser for fetch, we need
        // to clone the response.
        var fetchRequest = event.request.clone();

        return fetch(fetchRequest).then(
          function(response) {
            // Check if we received a valid response
            if(!response || response.status !== 200 || response.type !== 'basic') {
              return response;
            }

            // IMPORTANT: Clone the response. A response is a stream
            // and because we want the browser to consume the response
            // as well as the cache consuming the response, we need
            // to clone it so we have two streams.
            var responseToCache = response.clone();

            caches.open(CACHE_NAME)
              .then(function(cache) {
                cache.put(event.request, responseToCache);
              });

            return response;
          }
        );
      })
  );
});
This service worker's 'fetch' event listener intercepts all outgoing network requests. It attempts to serve the requests from the cache first, providing an offline experience. If the resource isn't in the cache, it fetches it from the network, caches a copy, and then returns the network response.
// Caching strategies
const CACHE_STRATEGY = {
  cacheOnly: (request) => caches.match(request),
  networkOnly: (request) => fetch(request),
  networkFirst: (request) => {
    return fetch(request).then(response => {
      if (!response) throw new Error('Network response was not ok');
      return caches.open(CACHE_NAME).then(cache => {
        cache.put(request, response.clone());
        return response;
      });
    }).catch(() => caches.match(request));
  },
  cacheFirst: (request) => {
    return caches.match(request).then(response => {
      return response || fetch(request).then(response => {
        return caches.open(CACHE_NAME).then(cache => {
          cache.put(request, response.clone());
          return response;
        });
      });
    });
  },
  // ... additional strategies
};
This code defines a set of caching strategies that can be used by the service worker to determine how it handles fetch requests. These strategies provide different trade-offs between speed (cache) and freshness (network).