PWA Architecture and Capabilities

Module 25: Frontend Frameworks & State Management

Introduction to Progressive Web Apps

Progressive Web Apps (PWAs) represent a modern approach to web application development that combines the best of web and mobile applications. They are web applications that leverage modern web capabilities to deliver an app-like experience to users.

What Makes an App "Progressive"?

The term "progressive" refers to the idea that these applications work for every user, regardless of browser choice, because they're built with progressive enhancement as a core principle. They progressively enhance the user experience based on the capabilities of the user's device and browser.

Real-world analogy: Think of PWAs like adaptable vehicles that can transform based on the road conditions. On smooth highways (modern browsers), they perform like high-end sports cars with all features enabled. On rough terrain (older browsers), they function more like rugged jeeps, losing some fancy features but still getting you to your destination.

graph TD A[Traditional Web App] --> B[Progressive Web App] C[Native Mobile App] --> B B --> D[Reliable/Resilient] B --> E[Fast/Responsive] B --> F[Engaging/App-like] style A fill:#f5f5f5 style B fill:#4285F4 style C fill:#f5f5f5 style D fill:#0F9D58 style E fill:#0F9D58 style F fill:#0F9D58 classDef default stroke:#333,stroke-width:2px;

Historical Context

The concept of PWAs was introduced by Google in 2015, with Alex Russell and Frances Berriman coining the term. They identified the patterns emerging in modern web applications and formalized them into the PWA concept.

Since then, PWAs have gained significant traction, with major companies like Twitter, Starbucks, Pinterest, Uber, and many others adopting this approach to deliver better user experiences while reducing development costs compared to maintaining separate web and native applications.

Core Principles of PWAs

Progressive Web Apps are built on three fundamental principles:

1. Reliability

PWAs should load instantly and never show the "dinosaur" offline page, even in uncertain network conditions.

2. Performance

PWAs should respond quickly to user interactions with smooth animations and jank-free scrolling.

3. Engagement

PWAs should feel like a natural app on the device, with an immersive user experience.

graph TD A[Progressive Web App] --> B[Reliable] A --> C[Fast] A --> D[Engaging] B --> B1[Works Offline] B --> B2[Resilient to Network Issues] C --> C1[Quick Loading] C --> C2[Smooth Interactions] D --> D1[Installable] D --> D2[Push Notifications] D --> D3[App-like Interface] style A fill:#4285F4 style B fill:#0F9D58 style C fill:#F4B400 style D fill:#DB4437 classDef default stroke:#333,stroke-width:2px;

Technical Components of PWAs

PWAs are built using several key technologies that work together:

1. HTTPS

PWAs must be served over HTTPS to ensure security. This is a prerequisite for many PWA features like service workers and geolocation.

2. Web App Manifest

A JSON file that controls how the app appears when installed on a device:

// Example manifest.json
{
  "name": "Weather PWA",
  "short_name": "Weather",
  "description": "Weather forecast information",
  "start_url": "/index.html",
  "display": "standalone",
  "background_color": "#3E4EB8",
  "theme_color": "#2F3BA2",
  "icons": [
    {
      "src": "/images/icons/icon-72x72.png",
      "sizes": "72x72",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-96x96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-128x128.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-144x144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/images/icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

3. Service Workers

JavaScript files that run in the background, separate from the web page, enabling features like offline functionality and push notifications:

// Basic service worker registration
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js')
      .then(registration => {
        console.log('ServiceWorker registration successful with scope: ', 
          registration.scope);
      })
      .catch(error => {
        console.log('ServiceWorker registration failed: ', error);
      });
  });
}
graph LR A[Web Application] --> B[Service Worker API] A --> C[Web App Manifest] A --> D[HTTPS] B --> E[Caching] B --> F[Background Sync] B --> G[Push Notifications] C --> H[Installability] C --> I[App-like Experience] D --> J[Security] D --> K[Trust] style A fill:#4285F4 style B fill:#DB4437 style C fill:#F4B400 style D fill:#0F9D58 classDef default stroke:#333,stroke-width:2px;

4. Application Shell Architecture

A design approach that separates the app's core infrastructure (shell) from its content:

graph TD A[Application Shell] --> B[Cached Locally] A --> C[Loads Instantly] A --> D[Contains UI Framework] A --> E[Dynamic Content Placeholders] F[Dynamic Content] --> G[Fetched as Needed] F --> H[Updated Over Network] F --> I[Can Be Cached for Offline] style A fill:#4285F4 style F fill:#DB4437 classDef default stroke:#333,stroke-width:2px;

Key Capabilities and Features

Offline Functionality

PWAs can work even when the user has no internet connection or limited connectivity:

Real-world example: Google Maps PWA allows users to download and access maps offline, enabling navigation without continuous internet access. Similarly, Spotify's PWA caches music for offline listening.

Installability

PWAs can be added to a user's home screen, making them easily accessible:

// Check if the app can be installed
window.addEventListener('beforeinstallprompt', (event) => {
  // Prevent the default prompt
  event.preventDefault();
  
  // Store the event for later use
  let deferredPrompt = event;
  
  // Show your custom "Add to Home Screen" button
  installButton.style.display = 'block';
  
  installButton.addEventListener('click', () => {
    // Show the install prompt
    deferredPrompt.prompt();
    
    // Wait for the user to respond
    deferredPrompt.userChoice.then((choiceResult) => {
      if (choiceResult.outcome === 'accepted') {
        console.log('User accepted the install prompt');
      } else {
        console.log('User dismissed the install prompt');
      }
      
      // Clear the saved prompt
      deferredPrompt = null;
      
      // Hide the install button
      installButton.style.display = 'none';
    });
  });
});

Push Notifications

PWAs can send notifications to users even when the browser is closed:

// Request notification permission
function requestNotificationPermission() {
  Notification.requestPermission().then(permission => {
    if (permission === 'granted') {
      // Subscribe the user to push notifications
      subscribeToPushNotifications();
    }
  });
}

// Subscribe to push notifications
function subscribeToPushNotifications() {
  navigator.serviceWorker.ready.then(registration => {
    // Get the server's public key
    return registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: urlBase64ToUint8Array(publicKey)
    });
  })
  .then(subscription => {
    // Send the subscription to your server
    return fetch('/api/subscriptions', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(subscription)
    });
  })
  .catch(error => {
    console.error('Error subscribing to push notifications:', error);
  });
}

Background Sync

PWAs can defer actions until the user has stable connectivity:

// Register for background sync
navigator.serviceWorker.ready.then(registration => {
  if ('sync' in registration) {
    // Listen for form submissions
    document.querySelector('form').addEventListener('submit', event => {
      event.preventDefault();
      
      // Get the form data
      const formData = new FormData(event.target);
      
      // Store the data in IndexedDB
      saveFormDataToIndexedDB(formData)
        .then(() => {
          // Register a sync event
          return registration.sync.register('submit-form');
        })
        .then(() => {
          console.log('Background sync registered!');
          // Show the user that the data will be submitted
          showOfflineSubmissionConfirmation();
        })
        .catch(error => {
          console.error('Error registering background sync:', error);
          // Handle the error appropriately
        });
    });
  }
});

App-like Interactions

PWAs offer smooth animations and responsive interactions:

Discoverability

PWAs are discoverable through search engines and can be shared via URLs:

PWA Architecture Patterns

Application Shell Architecture

The app shell architecture separates the app's interface from its content:

// Example structure
|-- index.html          # App shell HTML
|-- styles/
|   |-- app-shell.css   # Core UI styles
|   |-- content.css     # Content-specific styles
|-- scripts/
|   |-- app-shell.js    # Shell functionality
|   |-- router.js       # Client-side routing
|   |-- api.js          # API interactions
|-- images/
|   |-- icons/          # App icons
|-- manifest.json       # Web app manifest
|-- sw.js               # Service worker

The shell typically includes:

Content is then loaded dynamically into this shell.

PRPL Pattern

PRPL is a pattern for structuring and serving Progressive Web Apps with an emphasis on performance:

graph TD A[PRPL Pattern] --> B[Push critical resources] A --> C[Render initial route] A --> D[Pre-cache routes] A --> E[Lazy-load resources] B --> F[HTTP/2 Server Push] B --> G[Inline Critical CSS] C --> H[App Shell First] C --> I[Minimal UI] D --> J[Service Worker Caching] E --> K[Dynamic Imports] E --> L[Route-Based Code Splitting] style A fill:#4285F4 classDef default stroke:#333,stroke-width:2px;

SPA + API Architecture

Many PWAs follow a Single Page Application (SPA) architecture with an API backend:

graph TD A[Client Side] --> B[SPA Framework] A --> C[Service Worker] A --> D[IndexedDB] E[Server Side] --> F[REST/GraphQL API] E --> G[Authentication] E --> H[Database] B <--> F C --> I[Caching] C --> J[Offline Support] D --> K[Local Data Storage] style A fill:#4285F4 style E fill:#DB4437 classDef default stroke:#333,stroke-width:2px;

PWA with Popular Frameworks

React PWA

React provides tools like Create React App that make building PWAs straightforward:

// Creating a React PWA
npx create-react-app my-pwa --template cra-template-pwa

Key components in a React PWA:

// Service worker registration in React
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://cra.link/PWA
serviceWorkerRegistration.register();

Angular PWA

Angular provides the @angular/pwa package that adds PWA support to an existing Angular project:

// Adding PWA support to an Angular project
ng add @angular/pwa

This automatically adds:

// Service worker registration in Angular
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    ServiceWorkerModule.register('ngsw-worker.js', {
      enabled: environment.production,
      // Register the ServiceWorker as soon as the app is stable
      // or after 30 seconds (whichever comes first).
      registrationStrategy: 'registerWhenStable:30000'
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Vue PWA

Vue CLI provides a PWA plugin that simplifies PWA creation:

// Creating a Vue PWA
vue create my-pwa
# Select the PWA feature during project creation

// Or add PWA to an existing Vue project
vue add pwa

The Vue PWA plugin provides:

// Service worker registration in Vue
// src/registerServiceWorker.js
/* eslint-disable no-console */

import { register } from 'register-service-worker'

if (process.env.NODE_ENV === 'production') {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready () {
      console.log('App is being served from cache by a service worker.')
    },
    registered () {
      console.log('Service worker has been registered.')
    },
    cached () {
      console.log('Content has been cached for offline use.')
    },
    updatefound () {
      console.log('New content is downloading.')
    },
    updated () {
      console.log('New content is available; please refresh.')
    },
    offline () {
      console.log('No internet connection found. App is running in offline mode.')
    },
    error (error) {
      console.error('Error during service worker registration:', error)
    }
  })
}

PWA Performance Optimization

Lighthouse Auditing

Lighthouse is a tool for improving the quality of web pages, including PWA features:

Critical Rendering Path Optimization

Optimizing the critical rendering path improves initial load performance:

Caching Strategies

Different resources benefit from different caching strategies:

Responsive Design Principles

PWAs should work well on all device sizes:

PWA Best Practices

Security Considerations

User Experience Guidelines

Testing PWAs

PWA Checklist

Google provides a comprehensive PWA checklist that includes:

  1. Site is served over HTTPS
  2. Pages are responsive on tablets & mobile devices
  3. All app URLs load while offline
  4. Metadata provided for Add to Home screen
  5. First load fast even on 3G
  6. Site works cross-browser
  7. Page transitions don't feel like they block on the network
  8. Each page has a URL

Meeting these criteria ensures your PWA provides a high-quality experience on all devices.

Real-World PWA Examples

Twitter Lite

Twitter Lite is a PWA that reduced data usage and load times:

Starbucks PWA

Starbucks built a PWA for their ordering system:

Pinterest PWA

Pinterest's PWA dramatically improved their mobile web experience:

Uber PWA

Uber's PWA (m.uber.com) loads quickly even on 2G networks:

Practice Activity

PWA Analysis

For this activity, you'll analyze an existing PWA to understand its architecture and capabilities:

  1. Choose one of the following PWAs to analyze:
    • Twitter Lite (mobile.twitter.com)
    • Spotify Web Player (open.spotify.com)
    • Starbucks (app.starbucks.com)
    • Pinterest (www.pinterest.com)
    • Instagram Web (www.instagram.com)
  2. Using Chrome DevTools, analyze the PWA:
    • Examine the manifest.json file (Application tab > Manifest)
    • Look at the registered service worker (Application tab > Service Workers)
    • Check the caching strategy (Application tab > Cache Storage)
    • Run a Lighthouse audit (Lighthouse tab > Generate report)
  3. Document your findings:
    • What capabilities does the PWA implement? (offline support, push notifications, etc.)
    • How is the app shell structured?
    • What caching strategies are used?
    • How is the application optimized for performance?
    • What could be improved based on the Lighthouse audit?

PWA Setup Exercise

In this exercise, you'll add the basic PWA capabilities to a web application:

  1. Create a new project using your preferred framework:
    • React: npx create-react-app my-pwa --template cra-template-pwa
    • Angular: ng new my-pwa then ng add @angular/pwa
    • Vue: vue create my-pwa (select PWA option)
    • Or use a simple HTML/JS/CSS project
  2. Verify the manifest.json file has all required fields:
    • name, short_name
    • start_url
    • display (standalone or fullscreen)
    • background_color, theme_color
    • icons in various sizes
  3. Ensure service worker registration is enabled
  4. Run a Lighthouse audit to check PWA compliance
  5. Make any necessary improvements based on the audit results

Key Takeaways

Additional Resources