Progressive Web Apps (PWAs) are websites that use a collection of modern web technologies to provide app-like user experiences. Typical PWA features include offline support, background sync and the ability to “install” the site as a mobile app.

PWA is a general term that’s used to reference multiple technologies. A PWA will usually include a Web App Manifest and a Service Worker. The Web App Manifest provides data used to register the site with an operating system’s app list. The Service Worker can be used to add features which stay alive after the site’s browser tabs are closed.

Not every site will use all the possible capabilities. Some will include a Web App Manifest but no Service Worker; others will have a Service Worker but no app installation support. There’s no particular standard defining how a PWA should behave. A PWA is any site which uses some of these technologies to offer some level of feature parity with native apps.

Understanding “Progressive” Enhancement

The “progressive” part of Progressive Web Apps derives from their expected use of progressive enhancement. The PWA features are still fairly new and may not be supported in all browsers. A PWA should continue to provide its core web-based experience even if browser APIs such as service workers are unavailable.

Features such as push notifications shouldn’t be a mandatory part of your experience. If your site fails to register for push notifications, the rest of the site should continue to function normally. When the browser does support modern technologies, your site should treat them as an enhancement that gives the user extra convenience.

Feature Availability

PWA features provide deeper integration between your website and the user’s device. Consequently, the APIs used to create them are restricted to HTTPS connections. This helps ensure content hasn’t been tampered with to persistently install nefarious apps on your device.

Individual features are gated behind user-facing web permissions. Capabilities such as push notifications could be abused to bombard the user with unwanted content. Users need to acknowledge a permission prompt that grants your site the ability to use potentially intrusive features.

These conditions mean you need to use feature detection before trying to use most of the relevant APIs. A feature might be unavailable because the browser doesn’t support it. However, it might also be disabled due to an insecure connection, or because the user denied a permission prompt. You cannot anticipate the latter scenario; the user might change their mind at any time.

PWA Traits

Although there’s no standard for PWAs, most sites offering an app-like experience have the following traits:

  • Installable – On supported platforms, the website can be “installed” like a mobile app. The effects vary by operating system and browser.
  • Works Offline – Service Workers are scripts which continue to run even when your site isn’t open. They can also intercept network requests, allowing you to serve essential assets from a cache. This lets your site keep working when the user’s offline.
  • App-Like UI – Although not a requirement, PWAs are likely to place greater emphasis on using modern UI controls which integrate well with native app styles. PWAs will need to be responsive to different screen sizes.

Beyond these core traits, individual PWAs will layer up additional features to create the user experience they require. These features may include push notifications, background uploads and periodic background data fetches. These optional capabilities let you re-engage the user and anticipate their needs.

Web App Manifest

Web App Manifests are JSON files defining key information about your site. This includes details such as site name, accent colour and icon file locations.

Browsers and operating systems use the information to create system-level app entries when you “install” your site. This is how your site gets an app drawer icon on Android or a Start menu link on Windows.

Here’s an example manifest:

{"name": "Example App",
    "display": "standalone",
    "background_color": "#fff",
    "description": "My App",
    "start_url": "/",
    "icons": [
            "src": "/icon.png",
            "sizes": "512x512",
            "type": "image/png"

The display property declares that the site should be launched in its own standalone window when it’s opened from an app drawer. The start_url property defines the initial URL to navigate to after launch.

Your site must advertise its Web App Manifest using a link tag in its HTML:

<link rel="manifest" href="/manifest.json">

Browsers will then attempt to retrieve the manifest. If it’s valid, the user may be suggested to “install” the app. On Chrome for Android, this appears as a “Add to Homescreen” bar. The bar’s not guaranteed to appear – Chrome uses heuristics such as the frequency you visit the site to determine whether an install hint should be shown.

Service Worker

Service Workers are created using JavaScript. They stand apart from your site’s main JavaScript. A service worker is registered from your main script. The browser then keeps the service worker running when the tab is closed. This allows you to run background operations that can outlive the user’s visit to your site.

Here’s how you register a service worker:

if ("serviceWorker" in navigator) {

The service-worker.js script will be downloaded and activated as a service worker. A service worker doesn’t offer any useful functionality on its own. You need to use APIs such as Caches, Background Sync or Push Notifications within your service worker.

Here’s a basic example that caches all the assets associated with our app:

self.addEventListener("install", e => {
    e.waitUntil(async () => {
        const cache = await"my-app-cache");
        await cache.addAll([
self.addEventListener("fetch", e => {
    e.respondWith(async () => {
        const cached = await catches.match(e.request);
        if (cached) return cached;
        const response = await fetch(e.request);
        return response;

This uses the install service worker lifecycle event to download all the critical assets. They’ll be added to a dedicated cache which is controlled by the service worker.

The service worker also listens to the fetch event. This occurs whenever a network request is made. The event object includes a request property which is a request object accepted by the fetch() API.

The code first checks whether the request already exists in the cache. If it’s a URL that was cached earlier, this check will pass and the cached response will be returned. Otherwise, the request is forwarded to the fetch() API. The asset will be loaded over the network as normal.

This example only exhibits the tip of what service workers can offer. In a real app, you’ll often want more advanced caching, extra background capabilities and additional lifecycle events (to control how your service worker supersedes, and is superseded by, other versions).


Progressive Web Apps are web applications which use a set of related browser APIs to offer an app-like experience. You’ve probably used PWAs in the past, even if you weren’t consciously aware of it.

The PWA experience is founded on progressive enhancement and user choice. You need to provide consent before each site can activate its PWA features. Otherwise, the site should provide its core browser-based experience, without the extra capabilities provided by PWA technologies.

Profile Photo for James Walker James Walker
James Walker is a contributor to How-To Geek DevOps. He is the founder of Heron Web, a UK-based digital agency providing bespoke software development services to SMEs. He has experience managing complete end-to-end web development workflows, using technologies including Linux, GitLab, Docker, and Kubernetes.
Read Full Bio »