skip to content

How should an Angular app use SwUpdate's versionUpdates, checkForUpdate() and activateUpdate() to move users to a new release, and why is reloading preferred?

level: seniorimportance: must knowfreq 36%

answer

  1. filter for VERSION_READY
  2. prompt, then reload
  3. check on a schedule once stable
  4. shell and chunk mismatch
  5. unrecoverable needs a reload too

basics

~10 s

Subscribe to versionUpdates, react to VERSION_READY by prompting the user and reloading, call checkForUpdate() for long-lived tabs, and avoid activateUpdate() without a reload because the running shell can mismatch new lazy chunks.

solid answer

~40 s

`SwUpdate` from `@angular/service-worker` is the app-side API. `versionUpdates` emits `VERSION_DETECTED`, `VERSION_READY`, `VERSION_INSTALLATION_FAILED` and `NO_NEW_VERSION_DETECTED`; the app filters for `VERSION_READY`, which carries `currentVersion` and `latestVersion`, and prompts the user to reload. `checkForUpdate()` asks the worker to check now and resolves `true` when a new version is ready, useful for tabs that stay open for days; start polling only after the app is stable so registration is not delayed. `activateUpdate()` switches the current tab to the latest version without reloading, but the documentation warns this can break the app: the running shell may request lazy chunks whose filenames changed. Also subscribe to `unrecoverable` and reload when the served version cannot recover. When the worker is disabled, `isEnabled` is `false`.

code

ts · 22 lines
ts
import { Injectable, inject } from '@angular/core';
import { SwUpdate, VersionReadyEvent } from '@angular/service-worker';
import { filter } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class UpdatePrompt {
  private readonly updates = inject(SwUpdate);

  start(): void {
    if (!this.updates.isEnabled) return;

    this.updates.versionUpdates
      .pipe(filter((e): e is VersionReadyEvent => e.type === 'VERSION_READY'))
      .subscribe(() => {
        if (confirm('A new version is available. Reload now?')) {
          document.location.reload();
        }
      });

    this.updates.unrecoverable.subscribe(() => document.location.reload());
  }
}

go deeper

for a junior

Recall that SwUpdate.versionUpdates tells the app a new version is ready and that the usual response is to reload.

for a middle

Explain the four version event types, checkForUpdate's promise, and why activateUpdate without a reload is risky.

for a senior

Design the update experience: when to prompt, polling for long-lived tabs after stability, and handling unrecoverable state.

for a principal

Set the release policy for how quickly clients must update, including forced reloads for critical fixes carried in appData.

## What SwUpdate is for The Angular service worker downloads new versions in the background and serves them on the next load. `SwUpdate`, injected from `@angular/service-worker`, lets the running app **observe and influence** that process. It is available once `provideServiceWorker()` is in the application providers. ## versionUpdates: knowing a version is ready `versionUpdates` is an `Observable` of four event types: | Event type | Meaning | |---|---| | `VERSION_DETECTED` | A new version was found on the server and is about to download | | `VERSION_READY` | The new version is downloaded and ready; carries `currentVersion` and `latestVersion` (each with `hash` and optional `appData`) | | `VERSION_INSTALLATION_FAILED` | Downloading or installing the new version failed; carries an `error` string | | `NO_NEW_VERSION_DETECTED` | A check found nothing new | `VERSION_READY` is the one that drives user-facing behaviour. `appData` comes from `ngsw-config.json` and can carry release notes or a "critical" flag for the prompt. ## The recommended flow: prompt and reload 1. Filter `versionUpdates` for `VERSION_READY`. 2. Tell the user a new version is available, ideally at a moment that does not interrupt work (not mid-form). 3. On confirmation, call `document.location.reload()`. Reloading gives the tab a **consistent** new version: new `index.html`, new bundles, new lazy chunks together. ## activateUpdate(): why it is risky `activateUpdate()` updates the current tab to the latest ready version **without reloading**. The already-running JavaScript is still the old build, but subsequent requests are served from the new version. If the old code then loads a lazy route, it asks for a chunk filename from the old build that the new version may not contain, and the app breaks. The API documentation says to use it only when you are certain it is safe for your case. It resolves `true` if an update was activated and `false` if none was available. ## checkForUpdate(): for long-lived tabs The worker checks for updates during initialization and on navigation requests. A tab left open for days, such as a kiosk or an operations dashboard, may never trigger either. `checkForUpdate()` asks the worker to check now: - resolves `true` when a new version was found and is ready to activate; - resolves `false` when there is nothing new; - rejects on error, or immediately when the service worker is not enabled. Start periodic checks only **after the application is stable**. By default the worker is registered when the app stabilizes, with a 30-second cap (`registerWhenStable:30000`). In a zone-based app, a timer started at startup keeps it from stabilizing, so registration waits for the cap. The documented pattern waits for `ApplicationRef.isStable` to emit `true`, then starts an interval. ## unrecoverable: when reloading is the only fix `unrecoverable` emits when the version serving this tab is broken beyond repair, for example when the browser evicted a lazy chunk of an old version that the server no longer has. The event carries a `reason`. The only fix is a full reload, so the handler should tell the user and reload. ## When the worker is not active If the service worker is disabled (for example `enabled: !isDevMode()` in development) or unsupported, `SwUpdate.isEnabled` is `false`, `versionUpdates` and `unrecoverable` never emit, and the methods reject. Code that injects `SwUpdate` should check `isEnabled` before calling methods, so development builds do not log errors. ## Common mistakes - **Reloading on `VERSION_DETECTED`**: the new version is not downloaded yet, so the reload is served the old version again. - **Reloading without asking** in the middle of user input, losing unsaved work. - **Polling from app startup** in a zone-based app, delaying worker registration until the 30-second cap. - **Treating `activateUpdate()` as a soft reload**: it changes which version answers future requests, not the code already running. - **Calling `SwUpdate` methods in development** without checking `isEnabled`, producing rejected promises in the console. ## Putting it together A typical app has one root-provided service that: - subscribes to `versionUpdates` and prompts on `VERSION_READY`; - subscribes to `unrecoverable` and forces a reload; - in long-lived contexts, polls `checkForUpdate()` after stability; - never calls `activateUpdate()` unless the app has no lazy code or has proven it safe.

  • What exactly can break after activateUpdate() without a reload?
    The code already running in the tab is the old build. After activation the worker serves the new version's files, so when old code lazy-loads a route it may request a chunk filename that only existed in the old build. That request fails and the navigation breaks, which is why the docs recommend reloading.
  • Why can calling checkForUpdate() on an interval from startup delay service worker registration?
    The default strategy registers when the app becomes stable, capped at 30 seconds. In a zone-based app a recurring timer keeps it from stabilizing, so registration waits for the cap. Waiting for `ApplicationRef.isStable` before starting the interval avoids the delay.

saying these in an interview costs you the question

  • activateUpdate() is the safest way to update a running tab
  • VERSION_DETECTED means the new version is ready to use
  • checkForUpdate() also reloads the page when it finds a version
  • SwUpdate methods work the same when the worker is disabled
  • unrecoverable can be fixed by calling checkForUpdate()