pwa-patterns

Progressive Web App patterns using Workbox 7.x for service worker management, offline-first strategies, and app-like experiences.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "pwa-patterns" with this command: npx skills add yonatangross/orchestkit/yonatangross-orchestkit-pwa-patterns

PWA Patterns

Progressive Web App patterns using Workbox 7.x for service worker management, offline-first strategies, and app-like experiences.

Service Worker Lifecycle

Installing -> Waiting -> Active │ │ │ install activated fetch events (precache) when old SW (runtime cache) is gone

Workbox: Generate Service Worker

// build-sw.js (Node.js) const { generateSW } = require('workbox-build');

async function buildServiceWorker() { await generateSW({ globDirectory: 'dist/', globPatterns: ['**/*.{html,js,css,png,jpg,json,woff2}'], swDest: 'dist/sw.js', clientsClaim: true, skipWaiting: true, navigateFallback: '/index.html', navigateFallbackDenylist: [/^/api//], runtimeCaching: [ { urlPattern: /^https://api.example.com//, handler: 'NetworkFirst', options: { cacheName: 'api-cache', networkTimeoutSeconds: 10 }, }, { urlPattern: /.(?:png|jpg|jpeg|svg|gif|webp)$/, handler: 'CacheFirst', options: { cacheName: 'images', expiration: { maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60 } }, }, ], }); }

Caching Strategies

// CacheFirst: Static assets that rarely change registerRoute(/.(?:js|css|woff2)$/, new CacheFirst({ cacheName: 'static-v1', plugins: [new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 365 * 24 * 60 * 60 })], }));

// NetworkFirst: API calls (fresh data preferred) registerRoute(//api//, new NetworkFirst({ cacheName: 'api-cache', networkTimeoutSeconds: 10, plugins: [new CacheableResponsePlugin({ statuses: [0, 200] })], }));

// StaleWhileRevalidate: User avatars, non-critical images registerRoute(//avatars//, new StaleWhileRevalidate({ cacheName: 'avatars' }));

// NetworkOnly: Auth endpoints registerRoute(//auth//, new NetworkOnly());

VitePWA Integration

// vite.config.ts import { VitePWA } from 'vite-plugin-pwa';

export default defineConfig({ plugins: [ VitePWA({ registerType: 'autoUpdate', workbox: { globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'], runtimeCaching: [{ urlPattern: /^https://api./, handler: 'NetworkFirst' }], }, manifest: { name: 'My PWA App', short_name: 'MyPWA', theme_color: '#4f46e5', icons: [ { src: '/icon-192.png', sizes: '192x192', type: 'image/png' }, { src: '/icon-512.png', sizes: '512x512', type: 'image/png' }, ], }, }), ], });

Web App Manifest

{ "name": "My Progressive Web App", "short_name": "MyPWA", "start_url": "/", "display": "standalone", "background_color": "#ffffff", "theme_color": "#4f46e5", "icons": [ { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" } ] }

React Hooks

Install Prompt Hook

import { useState, useEffect } from 'react';

interface BeforeInstallPromptEvent extends Event { prompt: () => Promise<void>; userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>; }

export function useInstallPrompt() { const [installPrompt, setInstallPrompt] = useState<BeforeInstallPromptEvent | null>(null); const [isInstalled, setIsInstalled] = useState(false);

useEffect(() => { const handler = (e: BeforeInstallPromptEvent) => { e.preventDefault(); setInstallPrompt(e); }; window.addEventListener('beforeinstallprompt', handler as EventListener); if (window.matchMedia('(display-mode: standalone)').matches) setIsInstalled(true); return () => window.removeEventListener('beforeinstallprompt', handler as EventListener); }, []);

const promptInstall = async () => { if (!installPrompt) return false; await installPrompt.prompt(); const { outcome } = await installPrompt.userChoice; setInstallPrompt(null); if (outcome === 'accepted') { setIsInstalled(true); return true; } return false; };

return { canInstall: !!installPrompt, isInstalled, promptInstall }; }

Offline Status Hook

export function useOnlineStatus() { const [isOnline, setIsOnline] = useState(navigator.onLine);

useEffect(() => { const handleOnline = () => setIsOnline(true); const handleOffline = () => setIsOnline(false); window.addEventListener('online', handleOnline); window.addEventListener('offline', handleOffline); return () => { window.removeEventListener('online', handleOnline); window.removeEventListener('offline', handleOffline); }; }, []);

return isOnline; }

Background Sync

// sw.js import { BackgroundSyncPlugin } from 'workbox-background-sync'; import { registerRoute } from 'workbox-routing'; import { NetworkOnly } from 'workbox-strategies';

registerRoute( //api/forms/, new NetworkOnly({ plugins: [new BackgroundSyncPlugin('formQueue', { maxRetentionTime: 24 * 60 })] }), 'POST' );

Anti-Patterns (FORBIDDEN)

// NEVER: Cache everything with no expiration (storage bloat) // NEVER: Skip clientsClaim (old tabs stay on old SW) // NEVER: Cache authentication tokens (security risk) // NEVER: Precache dynamic content (changes frequently) // NEVER: Forget offline fallback for navigation // NEVER: Cache POST responses

PWA Checklist

  • Service worker registered

  • Manifest with icons (192px + 512px maskable)

  • HTTPS enabled

  • Offline page works

  • Responsive design

  • Fast First Contentful Paint (< 1.8s)

Key Decisions

Decision Recommendation

SW generator generateSW for simple, injectManifest for custom

API caching NetworkFirst for critical data

Static assets CacheFirst with versioned filenames

Update strategy Prompt user for major changes

Related Skills

  • caching-strategies

  • Backend caching patterns

  • core-web-vitals

  • Performance metrics

  • streaming-api-patterns

  • Real-time updates

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

responsive-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

domain-driven-design

No summary provided by upstream source.

Repository SourceNeeds Review
General

dashboard-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
General

rag-retrieval

No summary provided by upstream source.

Repository SourceNeeds Review