Swoff Swoff

Offline Capability & SW

Understanding offline capability and the service workers that make it possible.

What Offline Capability Means

Most developers think offline capability means "caching assets so the page loads without internet." That's incomplete. For Swoff, an offline-capable app ensures:

  • Static assets & routes load — all HTML, CSS, JS, images served via Service Worker
  • App logic runs — JavaScript executes in the browser
  • Dynamic data handled intelligently — reads cached, mutations queued when offline
  • Navigation works — all routes accessible without network

The Offline Spectrum

LevelWhat WorksExample
NoneNothing without internetTraditional server-rendered apps
PartialAssets cached, but data needs networkBasic PWA with no local storage
App ShellAssets + all routes offline, reads cached, mutations queued when offlineSwoff apps

Why Most PWAs Fail at Offline

  1. Cache assets but not data — app loads, then crashes trying to fetch API
  2. Rely on runtime cachingstale-while-revalidate doesn't help if cache is empty
  3. Don't version their SW — browser auto-updates break things silently
  4. Don't handle failed fetches — no fallback UI for missing data

What is a Service Worker?

A service worker (SW) is a JavaScript file that runs in the background, separate from your web page. It is the foundation of offline capability:

  • Runs in the background — separate from the main thread
  • Intercepts network requests — can serve cached responses
  • No DOM access — communicates with your app via postMessage
  • Persists after page close — stays alive until explicitly removed
  • HTTPS required — except localhost for development

SW Lifecycle

Register → Install → Activate → Fetch
  1. Register — Your app registers the SW
  2. Install — SW downloads and caches assets
  3. Activate — Old SW cleaned up, new SW takes over
  4. Fetch — Intercepts requests, serves from cache

The Problem with Default SW Behavior

Browsers automatically download new SW files when detected, install them in the background, and activate on the next reload. If your new SW has breaking changes, users get them without warning.

Swoff's Solution: Versioned SW Files

Instead of /sw.js, we use /sw-v1.0.0.js, /sw-v1.0.1.js, etc. Each version is a unique file. The browser only loads the version you tell it to.

package.json version: 1.0.0

Build generates: sw-v1.0.0.js + version.json

User visits app → App checks version.json

Same version? → Use existing SW
New version?  → Show update prompt → User approves → Register new version

See Versioned SW System for implementation details.

Versioned App vs PWA Install

These are two completely different things that people confuse because both use the word "install":

PWA Install (User Experience)

Creates a shortcut on the user's device. The app opens in a standalone window without browser UI. This is purely cosmetic — it doesn't change what the app does.

App Version Update (Technical)

Replaces the code with a new version. This is functional — it changes what the app does.

AspectPWA InstallApp Version Update
WhatCreates shortcutReplaces code
TriggerUser choosesDeveloper releases
FrequencyOnce per deviceEvery release
EffectUI changesFunctionality changes
OfflineDoesn't helpMakes it possible

You can have an offline app without PWA install (user accesses via bookmark). You can have versioned updates without PWA. Swoff treats these as independent features — versioned updates are core, PWA is optional.

Key SW Events

EventWherePurpose
installSWCache assets
activateSWClean up old caches
fetchSWIntercept requests
messageSWReceive messages from client
controllerchangeClientSW changed

Important Limitations

  1. SW can't access DOM — use postMessage to communicate with the page
  2. SW is terminated when idle — don't rely on persistent state
  3. SW scope is its location/sw.js controls all pages under /

Next Steps

On this page