Skip to content
Angular ng pwa 4 min read

Introduction to PWAs

A Progressive Web App (PWA) is a regular web application that uses modern browser capabilities to feel like a native app: it can be installed to the home screen, launched from its own window, work offline, and receive push notifications. Angular ships first-class PWA support through the @angular/pwa package, which layers a production-ready service worker on top of your existing build. This page explains what makes an app “progressive” and how Angular’s service worker turns an ordinary single-page app into an installable, offline-capable experience.

What makes an app a PWA

PWAs are defined by capabilities rather than a single API. An app qualifies as installable and offline-capable when it satisfies a small set of baseline requirements:

RequirementPurpose
HTTPS (or localhost)Service workers only run in secure contexts
Web app manifestDeclares name, icons, theme color, and display mode
Service workerIntercepts requests to enable caching and offline use
Responsive UIAdapts to phones, tablets, and desktops
Fast first loadCached assets make repeat visits near-instant

The two pieces that do the heavy lifting are the manifest (which the browser reads to offer installation) and the service worker (a background script that sits between your app and the network). Angular generates both for you.

The service worker

A service worker is a JavaScript file the browser runs in a separate thread, independent of any open tab. Once registered, it can intercept every outgoing network request and decide whether to serve a cached response or hit the network. Because it persists after the page closes, it can also power background sync and push notifications.

Writing a correct service worker by hand is notoriously error-prone — cache invalidation, versioning, and update flows all have sharp edges. Angular avoids this by providing the Angular service worker (@angular/service-worker), a fully managed worker that is configured declaratively through a JSON file (ngsw-config.json) instead of imperative code.

Angular’s service worker is opt-in and only registers in production builds by default. This prevents stale caches from confusing you during development.

Adding PWA support

The fastest way to make an existing Angular project a PWA is the official schematic:

ng add @angular/pwa

This single command wires up everything needed:

CREATE ngsw-config.json
CREATE public/manifest.webmanifest
CREATE public/icons/icon-72x72.png (and other sizes)
UPDATE angular.json
UPDATE src/index.html
UPDATE src/app/app.config.ts

In a modern standalone app, the schematic registers the service worker provider in app.config.ts:

import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideServiceWorker } from '@angular/service-worker';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideServiceWorker('ngsw-worker.js', {
      enabled: !isDevMode(),
      registrationStrategy: 'registerWhenStable:30000',
    }),
  ],
};

The registrationStrategy of registerWhenStable:30000 tells Angular to wait until the app is stable (no pending tasks) before registering, falling back to a 30-second timeout. This keeps the service worker from competing with your app’s initial render.

Building and testing it

The service worker is excluded from ng serve, so you must produce a production build and serve the static files over HTTP to see it work:

ng build
npx http-server -p 8080 -c-1 dist/<project-name>/browser

Open http://localhost:8080, then visit the browser DevTools Application tab. Under Service Workers you should see ngsw-worker.js activated. Reload once, switch the Network panel to Offline, and reload again — the app still loads because its assets were cached:

Output:

Service Worker: ngsw-worker.js — #activated and running
Cache Storage:
  ngsw:1:assets   (app shell, JS, CSS, icons)
  ngsw:1:data     (runtime API responses, if configured)
App loads with network Offline ✓

How caching is configured

The ngsw-config.json file controls what gets cached and when. Asset groups handle your build output; data groups handle API calls. A trimmed example:

{
  "$schema": "./node_modules/@angular/service-worker/config/schema.json",
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app",
      "installMode": "prefetch",
      "resources": {
        "files": ["/favicon.ico", "/index.html", "/*.css", "/*.js"]
      }
    },
    {
      "name": "assets",
      "installMode": "lazy",
      "updateMode": "prefetch",
      "resources": {
        "files": ["/assets/**", "/*.(svg|png|webp|woff2)"]
      }
    }
  ]
}

prefetch downloads resources immediately at install time so the app works offline on the very next visit; lazy only caches resources after they are first requested. Tuning these modes is how you balance offline coverage against initial download size.

The web app manifest

The generated manifest.webmanifest is what makes the app installable. Browsers read it to show an install prompt and to configure the standalone window:

{
  "name": "DevCraftly Demo",
  "short_name": "DevCraftly",
  "start_url": "./",
  "display": "standalone",
  "theme_color": "#1976d2",
  "background_color": "#fafafa",
  "icons": [
    { "src": "icons/icon-192x192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "icons/icon-512x512.png", "sizes": "512x512", "type": "image/png" }
  ]
}

The display: "standalone" value hides the browser chrome so the launched app looks native. A maskable 512×512 icon is required for the install prompt to appear on most platforms.

Best Practices

  • Always test PWA behavior against a production build served over HTTPS or localhostng serve never registers the service worker.
  • Keep enabled: !isDevMode() so caches never go stale while you develop.
  • Prefer prefetch for the core app shell and lazy for large optional assets to keep the first install lightweight.
  • Provide both 192×192 and a maskable 512×512 icon so browsers reliably offer installation.
  • Clear old caches by bumping your app version on each deploy — Angular’s worker handles cache busting automatically when build hashes change.
  • Run Lighthouse’s PWA audit in CI to catch a missing manifest field or icon before it ships.
Last updated June 14, 2026
Was this helpful?