Home / Articles

Enable Offline Support in Web Apps with Service Workers

Service workers and the Cache API make a site load instantly and keep working offline. Writing the worker, registering it and picking caching strategies.

Publisher: REACTAPP.TOP5 min read1098 words

This article is published in English.

Sources: ☕ JavaScript Hack That Makes Any Website Work Offline

Picture someone browsing your site while riding a train, their signal dropping in and out every few seconds. On most websites, that flaky connection means a dead end — a browser error telling them they're offline. What if your site could sidestep that problem entirely and keep working anyway?

This isn't some experimental future feature. It's achievable right now using a browser technology called a Service Worker. A Service Worker behaves like a scriptable middleman that stands between your web app and the network. It can grab outgoing requests before they ever reach the server and answer them itself using files stored locally, which lets your site keep functioning even with no connection at all.

This trick does more than add a nice-to-have fallback — it can noticeably speed up load times and make your app far more resilient to bad networks. And despite sounding advanced, the setup is fairly straightforward.

What Powers This Trick: Service Workers Paired with the Cache API

Two browser features work together to make this possible:

  1. Service Worker: a background script that runs independently of your page's main thread, functioning as a stand-in network layer.
  2. Cache API: a built-in browser interface that lets you store and retrieve resources programmatically, much like a key-value store. This is where you'll stash the HTML, CSS, JavaScript, images, and other assets your site depends on.

Here's the general flow: the first time someone visits your site while connected to the internet, the Service Worker installs itself and pulls down the important files into its cache. After that point, every network request your page makes passes through the Service Worker first. When the visitor loses their connection, the worker responds using whatever it already has cached instead of trying — and failing — to reach the server, so the page still renders almost instantly.

Step 1: Write the Service Worker Script

Start by creating a file called sw.js at the root of your project. This is the script responsible for installing the cache and intercepting future requests.

// sw.js

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

// The 'install' event is fired when the service worker is first installed.
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        console.log('Opened cache');
        return cache.addAll(urlsToCache);
      })
  );
});

// The 'fetch' event is fired for every network request.
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(response => {
        // If the request is in the cache, serve the cached version.
        if (response) {
          console.log('Serving from cache:', event.request.url);
          return response;
        }

        // Otherwise, fetch from the network.
        console.log('Fetching from network:', event.request.url);
        return fetch(event.request);
      })
  );
});

This file mainly listens for two lifecycle events:

  • install: this is where you open a named cache and populate it with the list of files you want available even without a network connection.
  • fetch: this event fires for every request the page makes. The worker checks whether a matching response already exists in the cache; if it does, that cached copy is returned, and if not, the request is sent through to the network as normal.

Step 2: Register the Worker From Your App

Once the worker script exists, the browser still needs to be told to use it. Add the following snippet to your main script file, or inline it inside a <script> tag in your HTML.

// Register the service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('Service Worker registered successfully:', registration);
      })
      .catch(error => {
        console.error('Service Worker registration failed:', error);
      });
  });
}

This block first checks whether the current browser even supports the Service Worker API, and if it does, registers your sw.js file. Note the path used to register it: /sw.js needs to live at the root of your site, because a Service Worker can only control the pages that fall under the scope of the directory it's registered from.

Verifying That It Works

To watch this in action, load your site in a browser that implements Service Workers — Chrome, Firefox, and Edge all qualify.

  • Open the browser's developer tools (F12, or Ctrl+Shift+I).
  • Switch to the Application panel.
  • In the sidebar, find Service Workers. Your sw.js file should show up there as active.
  • Click into Cache Storage next to confirm that your named cache exists and contains the files you listed.
  • Tick the "Offline" checkbox near the top of the Service Worker section.
  • Reload the page.

If everything is wired correctly, the page loads right away despite having no network access. The console output in dev tools will confirm that requests are being intercepted and answered straight from the cache rather than the network.

Taking the Pattern Further: Alternate Caching Strategies

What was just described is known as a cache-first, network-fallback approach — great for assets that rarely change, like static images or bundled scripts. But Service Workers aren't limited to that single pattern; a few other common strategies exist:

  • Network-first, cache-fallback: suited to content that changes frequently and needs to stay current. The worker attempts a network fetch first, and only falls back to whatever's cached if that request fails, say due to a poor connection.
  • Stale-while-revalidate: the worker immediately answers with whatever is already cached for speed, while simultaneously firing off a network request in the background to refresh the cache for the next time the page loads. This gives you fast responses now and updated content soon after.

Mastering this basic offline pattern also sets you up for building full Progressive Web Apps, where offline support is combined with capabilities like push notifications and the ability to install the site to a device's home screen like a native app.

Adding this pattern to your project is a relatively small investment for a meaningful payoff: instead of a dead connection breaking the experience outright, your users barely notice the interruption.