Progressive Web Apps: From Zero to Production

What Makes a PWA?

Progressive Web Apps combine web technologies with native app capabilities: offline support, installability, push notifications, and fast performance.

Web App Manifest

// manifest.json
{
  "name": "My Awesome App",
  "short_name": "AwesomeApp",
  "description": "A progressive web app",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#3b82f6",
  "orientation": "portrait-primary",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any maskable"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "shortcuts": [
    {
      "name": "New Item",
      "url": "/new",
      "icons": [{ "src": "/icons/new.png", "sizes": "96x96" }]
    }
  ],
  "screenshots": [
    {
      "src": "/screenshots/desktop.png",
      "sizes": "1280x720",
      "type": "image/png",
      "form_factor": "wide"
    }
  ]
}

Service Worker Registration

// main.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const registration = await navigator.serviceWorker.register('/sw.js', {
        scope: '/'
      });
      
      console.log('SW registered:', registration.scope);
      
      // Check for updates
      registration.addEventListener('updatefound', () => {
        const newWorker = registration.installing;
        newWorker.addEventListener('statechange', () => {
          if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
            // New version available
            showUpdateNotification();
          }
        });
      });
    } catch (error) {
      console.error('SW registration failed:', error);
    }
  });
}

Caching Strategies

// sw.js
const CACHE_NAME = 'app-v1';
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js',
  '/offline.html'
];

// Install: Cache static assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(STATIC_ASSETS);
    })
  );
  self.skipWaiting();
});

// Activate: Clean old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) => {
      return Promise.all(
        keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key))
      );
    })
  );
  self.clients.claim();
});

// Fetch: Stale-while-revalidate for API, cache-first for assets
self.addEventListener('fetch', (event) => {
  const { request } = event;
  
  if (request.url.includes('/api/')) {
    // Network first, fallback to cache
    event.respondWith(networkFirst(request));
  } else {
    // Cache first, fallback to network
    event.respondWith(cacheFirst(request));
  }
});

async function cacheFirst(request) {
  const cached = await caches.match(request);
  if (cached) return cached;
  
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match('/offline.html');
  }
}

async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}

Background Sync

// Queue failed requests for later sync
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-messages') {
    event.waitUntil(syncMessages());
  }
});

async function syncMessages() {
  const db = await openDB('app-db', 1);
  const messages = await db.getAll('pending-messages');
  
  for (const message of messages) {
    try {
      await fetch('/api/messages', {
        method: 'POST',
        body: JSON.stringify(message)
      });
      await db.delete('pending-messages', message.id);
    } catch {
      // Will retry on next sync
      break;
    }
  }
}

// Register sync from app
async function sendMessage(message) {
  const db = await openDB('app-db', 1);
  await db.add('pending-messages', message);
  
  const registration = await navigator.serviceWorker.ready;
  await registration.sync.register('sync-messages');
}

Push Notifications

// Request permission and subscribe
async function subscribeToPush() {
  const registration = await navigator.serviceWorker.ready;
  
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
  });
  
  // Send subscription to server
  await fetch('/api/push/subscribe', {
    method: 'POST',
    body: JSON.stringify(subscription)
  });
}

// Handle push in service worker
self.addEventListener('push', (event) => {
  const data = event.data?.json() || {};
  
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png',
      badge: '/icons/badge.png',
      data: { url: data.url },
      actions: [
        { action: 'open', title: 'Open' },
        { action: 'dismiss', title: 'Dismiss' }
      ]
    })
  );
});

self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  
  if (event.action === 'open') {
    event.waitUntil(
      clients.openWindow(event.notification.data.url)
    );
  }
});

Install Prompt

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  showInstallButton();
});

async function handleInstallClick() {
  if (!deferredPrompt) return;
  
  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;
  
  if (outcome === 'accepted') {
    console.log('App installed');
  }
  
  deferredPrompt = null;
}

Conclusion

PWAs bridge the gap between web and native. Start with a manifest and service worker, then progressively enhance with push notifications, background sync, and offline support.

๐Ÿ“šTechnical Insights & Industry Trends

Insights & Knowledge Hub

Explore in-depth articles on software development, digital transformation, and emerging technologies. Learn from real-world experience.

Browse by Topic

๐Ÿ…ฐ๏ธ
ArchitectureJan 2024

Why Angular SSR Beats React for SEO in 2024

A comprehensive comparison of server-side rendering implementations and their impact on search engine optimization, performance, and user experience.

๐Ÿ”Œ
ArchitectureDec 2023

Microservices: When to Use and When to Avoid

Understanding the trade-offs of microservices architecture and making the right choice for your organization's scale, complexity, and team structure.

๐Ÿ—๏ธ
System ArchitectureJan 2024

Essential Design Patterns for Enterprise Systems

Master the fundamental design patterns that every software architect should know for building scalable, maintainable enterprise applications.

๐Ÿ—๏ธ
System ArchitectureJan 2024

Domain-Driven Design: A Practical Guide

Learn how to apply Domain-Driven Design principles to create software that truly reflects business requirements and scales with organizational complexity.

๐Ÿ—๏ธ
System ArchitectureDec 2023

Event-Driven Architecture: Complete Implementation Guide

Build loosely coupled, scalable systems using event-driven architecture patterns with practical examples using Kafka, RabbitMQ, and Azure Service Bus.

๐Ÿ—๏ธ
System ArchitectureDec 2023

RESTful API Design: Best Practices and Patterns

Design APIs that developers love to use. Learn REST principles, versioning strategies, error handling, and documentation best practices.

Deep Dives into Key Areas

๐Ÿ—๏ธ

System Architecture

Design patterns and architectural decisions

12 Articles
โ˜๏ธ

Cloud Computing

AWS, Azure, and cloud-native development

8 Articles
โšก

Performance

Optimization and scalability techniques

6 Articles
๐Ÿค–

AI & Machine Learning

Practical AI applications in enterprise

5 Articles
๐Ÿ”’

Security

Best practices and compliance

7 Articles
๐Ÿ“ฑ

Modern Frontend

Angular, React, and UI/UX best practices

10 Articles

Have a Project in Mind?

Let's turn your ideas into reality. Schedule a free consultation to discuss how we can help transform your business.