# Add controlled offline behavior to learning pages

Canonical page: https://yishto.com/en/learn/application-platforms/pwa-offline-learning/

Author: [Yishto](https://yishto.com/en/about/)

Give public learning documents, static assets, and private requests separate policies, explaining service-worker lifecycle, versioned caches, offline fallbacks, and consistent updates.

Published: 2026-11-10
Updated: 2026-11-10

Offline learning pages depend on which responses are saved, how they update, and what failures show. A manifest describes installation information; it does not save lessons automatically. A controlled first version caches explicitly public learning content and explains which local version is being shown.

The case includes a public introduction, course images, and an authenticated progress endpoint. Sharing a website does not give them the same data policy. One blanket rule can mix private responses into public storage or combine incompatible HTML and scripts.

## Give storage layers distinct roles

HTTP caching involves browser behavior and response headers. The Cache API lets code store requests and responses. IndexedDB supports structured local data. They are separate mechanisms. Lesson responses can use the Cache API, while editable notes need their own durable model.

Classify resources first. Fingerprinted public scripts can reuse cached copies. Documents can request the network first and fall back to a saved response. Private progress requests retain authentication and need a separate offline-write design. This lesson excludes authenticated content from public offline caching.

Quotas and browser storage management affect availability. An offline-ready label should reflect readable local content, without promising permanent retention. Clearing browser data must produce an understandable empty state with a way to download again.

## Understand service-worker lifecycle

A service worker runs outside the DOM in a worker context. Deployment normally requires HTTPS, with localhost treated as a development exception. Registration path and scope limit controlled pages. A deeply nested script cannot arbitrarily control the whole site.

Initial registration includes installation and activation. Installation can prepare a small essential resource set. Existing pages may not immediately be controlled, so inspect their controller or reload. Workers can stop; global variables cannot be durable task records.

During an update, the old worker can continue controlling open pages while the new worker waits. Forcing replacement can combine old HTML with new scripts or removed resources. Keep versions consistent and offer a refresh after the user has saved input.

## Add an offline layer to the project

Start with a working online site containing two public lessons and a teaching image. Serve it through localhost, add and link a manifest, and check icons and installation requirements for the target browser. Test installation separately from offline reading.

Create the worker, register it after a capability check, and define its scope. Implement installation with a small list of known resources. Use event lifetime management so the browser knows installation work is pending. A missing essential resource must not leave a partial version labeled ready.

Then route fetch events only for defined methods, same-origin public paths, and eligible responses. Other requests use ordinary network handling. Provide an explicit missing-content fallback instead of an empty document that appears successful. Inspect registration, control, and cache entries, then test browser restart.

## Model network-first behavior offline

The Python example updates storage when a network result is available, otherwise returning a copy or an unavailable message. It implements no service worker, Response object, status-code handling, or browser lifecycle.

```python
cache = {"/learn/intro": "saved article"}
def read(path, online, response=None):
    if online and response is not None:
        cache[path] = response
        return response
    return cache.get(path, "offline unavailable")
assert read("/learn/intro", False) == "saved article"
assert read("/learn/new", False) == "offline unavailable"
assert read("/learn/intro", True, "updated") == "updated"
print(read("/learn/intro", False))
```

The output is `updated`. The initial offline read returns `saved article`; an unsaved path returns `offline unavailable`. After an update, offline reading returns the new body. A real handler must check success, public-resource eligibility, and cache policy before storing a response.

Define acceptable waiting and fallback behavior. A connectivity flag cannot prove that the service is reachable. Base fallback on the actual request outcome and inform the page when an older version is used.

## Coordinate version changes and cleanup

Put application versions in cache names. Install new resources into a new namespace and clean obsolete application-owned caches according to an activation policy. Do not delete unrelated caches or user notes as if they were old assets.

Record document versions or a defined update-time source so saved content can be identified. Keep a complete old copy when updating fails. Show an update notification and refresh only after input has been saved and the user accepts the transition.

Rollback depends on retained resources. Removing versioned files still referenced by an open old page can break it. Test multiple tabs, keeping one on the old page while another loads the update. Observe waiting and activation and check for partially updated states.

## Protect private requests and failure boundaries

The progress endpoint still verifies identity. Client cache routing is not authorization. On logout, clear account-related local state so a later user cannot see the earlier user's data. Keep tokens outside shareable URLs and public cache keys.

Bound public caching too. Large downloads should be user-triggered with progress and failure reporting rather than putting the entire library into installation precaching. Distinguish missing resources, insufficient storage, and network errors, with appropriate recovery actions.

During development, record the worker version and current controller so caches do not conceal changes. Developer-tool updates or unregistering help investigation, but final tests must replay ordinary user transitions. Forced refresh alone does not prove correct natural updates.

Route only defined requests, such as GET lesson reads. Query parameters may identify different content; document their meaning before normalizing cache keys. Respect response caching and privacy restrictions. Ineligible results use a network or unavailable path instead of forced insertion into a public cache.

## Exercise: verify four access paths

Test offline first visit, offline access after an online visit, offline access after a document update, and an old tab waiting for a new worker. Missing initial content must be explicit. Saved content stays readable, and failed updates preserve a complete older version.

Inspect entries to confirm that only allowed public resources are cached. Private progress responses must be absent. Clearing application asset caches must preserve structured notes. Finally test installation and reopening on target browsers and devices, recording differences. Python output verifies its policy model, not browser installability or worker control.

## Sources & further reading

- [Progressive web applications](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps)
- [Using service workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers)

Sources checked on 2026-11-10. Examples are teaching data, not production measurements.

## Related guides

- [Balance localization, SEO, and loading behavior](https://yishto.com/en/learn/web-interface/localization-and-web-performance/)
- [Build an operable interface from document structure](https://yishto.com/en/learn/web-interface/html-layout-accessibility/)
