Web Development 3d ago 4 views 4 min read

How to implement Service Workers for offline caching in a PWA

Configure a Service Worker to cache assets and enable offline functionality for your Progressive Web App. This guide covers registration, caching strategies, and runtime updates.

Riya K.
Updated 8h ago
Sponsored

Cloud VPS — scale in minutes

Instantly deploy SSD cloud VPS with guaranteed resources, snapshots and per-hour billing. Pay only for what you use.

Configure a Service Worker to cache assets and enable offline functionality for your Progressive Web App. These steps target Node 20.x and modern browsers supporting the standard API. You will create a registration script, define caching rules, and handle runtime updates.

Prerequisites

  • A local development environment with Node.js 20.x installed.
  • A static web application or a Laravel/React/Vue project.
  • Access to the project root directory via terminal.
  • A browser with Service Worker support enabled (Chrome, Firefox, Edge).

Step 1: Create the service-worker.js file

Create a new file named service-worker.js in the root of your project directory. This file acts as the intermediary between the browser and the network.

const CACHE_NAME = 'pwa-cache-v1';
const urlsToCache = [
  '/',
  '/index.html',
  '/css/styles.css',
  '/js/app.js',
  '/images/logo.png'
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        if (response) {
          return response;
        }
        return fetch(event.request);
      })
  );
});

self.addEventListener('activate', event => {
  const cacheWhitelist = [CACHE_NAME];
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

Step 2: Register the Service Worker

Register the worker in your main JavaScript entry point, typically index.html or a global app.js. You must ensure the script is served over HTTPS or localhost, as Service Workers are restricted to secure contexts.

// In index.html or app.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js')
      .then(registration => {
        console.log('ServiceWorker registration successful with scope: ', registration.scope);
      })
      .catch(registrationError => {
        console.log('ServiceWorker registration failed: ', registrationError);
      });
  });
}

Step 3: Implement a Cache First Strategy for Static Assets

Modify the fetch event listener to prioritize cached responses for static assets while falling back to the network for dynamic content. This ensures fast load times for images and stylesheets even when the network is unstable.

self.addEventListener('fetch', event => {
  // Skip non-GET requests
  if (event.request.method !== 'GET') {
    return;
  }

  event.respondWith(
    caches.match(event.request)
      .then(response => {
        if (response) {
          return response;
        }
        return fetch(event.request).then(response => {
          // Check if the response is a valid resource
          if (!response || response.status !== 200 || response.type !== 'basic') {
            return response;
          }
          // Add to cache for 1 year (31536000 seconds)
          const responseToCache = response.clone();
          caches.open(CACHE_NAME).then(cache => {
            cache.put(event.request, responseToCache);
          });
          return response;
        });
      })
  );
});

Step 4: Handle Background Sync for Offline Forms

Implement background sync to queue requests when the user is offline and send them once connectivity is restored. This is essential for mobile apps that need to submit data without an active connection.

if ('serviceWorker' in navigator && 'Sync' in window) {
  navigator.serviceWorker.ready.then(reg => {
    reg.sync.register('sync-form-data');
  });
}

Step 5: Update the Service Worker Dynamically

Ensure the browser detects changes to the Service Worker file. The browser automatically invalidates the old worker when the file hash changes, but you must trigger a re-registration if the worker is cached in the service worker cache storage.

// Optional: Force update on page reload if needed
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.getRegistrations().then(registrations => {
      for (let registration of registrations) {
        registration.unregister();
      }
    });
  });
}

Verify the installation

Open your browser DevTools and navigate to the Application tab. Select Service Workers from the left sidebar. You will see the registered worker with its scope and status. Click the "Update" button if the status shows "Waiting" to install the new version immediately.

Run the following command in the browser console to check registration status:

console.log(navigator.serviceWorker.getRegistrations());

You will see an array containing the registration object with the scope and scriptURL.

Troubleshooting

Error: "Failed to register Service Worker: Not allowed to load script resource"

This occurs when the file is served over HTTP instead of HTTPS or localhost. Service Workers require a secure context. Ensure your development server uses HTTPS (e.g., https://localhost:443) or that you are running on localhost or 127.0.0.1.

Error: "ServiceWorker is not registered"

Check that the fetch event listener is included in the worker file. Without the fetch listener, the worker does nothing. Also, verify the file path matches the register() call exactly.

Issue: Caching only the first load

If new assets are not cached, ensure the fetch event listener is inside the install event or the fetch event is handling the response correctly. The code in Step 3 ensures new responses are cloned and put into the cache.

Issue: Old worker not updating

Clear the browser cache and hard reload the page (Ctrl+F5). If the issue persists, manually unregister the worker via the DevTools Application tab and reload the page to trigger a fresh registration.

Sponsored

Windows Dedicated Server

High-performance Windows dedicated servers with licensed Windows Server, Remote Desktop access and enterprise-grade hardware.

Tags: JavaScriptCachingPWAService WorkersOffline
0
Was this helpful?

Related tutorials

Comments 0

Login to leave a comment.

No comments yet — be the first to share your thoughts.