Some_claude_skills pwa-expert
Progressive Web App development with Service Workers, offline support, and app-like behavior. Use for caching strategies, install prompts, push notifications, background sync. Activate on "PWA",
install
source · Clone the upstream repo
git clone https://github.com/curiositech/some_claude_skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/curiositech/some_claude_skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/pwa-expert" ~/.claude/skills/erichowens-some-claude-skills-pwa-expert && rm -rf "$T"
manifest:
.claude/skills/pwa-expert/SKILL.mdsource content
Progressive Web App Expert
Build installable, offline-capable web apps with Service Workers, smart caching, and native-like experiences.
When to Use This Skill
- Making a web app installable on mobile/desktop
- Implementing offline functionality
- Setting up Service Worker caching strategies
- Handling install prompts (
)beforeinstallprompt - Background sync for offline-first apps
- Managing PWA update flows
- Creating web app manifests
When NOT to Use This Skill
- Native app development → Use React Native, Flutter, or native SDKs
- General web performance → Use Lighthouse/performance auditing tools
- Server-side rendering issues → Use Next.js/framework-specific docs
- Push notifications only → Consider dedicated push notification services
- Simple static sites → PWA overhead may not be worth it
Core Concepts
What Makes a PWA Installable
- HTTPS (or localhost for dev)
- Web App Manifest with required fields
- Service Worker with fetch handler
- Icons (192×192 and 512×512 minimum)
The PWA Stack
┌─────────────────────────────────────────┐ │ Your App (React/Next.js) │ ├─────────────────────────────────────────┤ │ Service Worker (sw.js) │ │ ┌─────────────┐ ┌─────────────────┐ │ │ │ Cache │ │ Network Fetch │ │ │ │ Storage │ │ Handling │ │ │ └─────────────┘ └─────────────────┘ │ ├─────────────────────────────────────────┤ │ manifest.json │ │ (App identity, icons, display mode) │ └─────────────────────────────────────────┘
Web App Manifest
Complete manifest.json
{ "name": "Junkie Buds 4 Life", "short_name": "JB4L", "description": "Recovery support app", "start_url": "/", "scope": "/", "display": "standalone", "orientation": "portrait-primary", "background_color": "#1a1410", "theme_color": "#1a1410", "icons": [ { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, { "src": "/icons/icon-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" } ], "shortcuts": [ { "name": "Find Meetings", "short_name": "Meetings", "url": "/meetings?source=shortcut", "icons": [{ "src": "/icons/meetings-96.png", "sizes": "96x96" }] } ] }
Display Modes
| Mode | Description |
|---|---|
| No browser UI, full screen |
| App-like, no URL bar (recommended) |
| Some browser controls |
| Normal browser tab |
Link in HTML
<head> <link rel="manifest" href="/manifest.json" /> <meta name="theme-color" content="#1a1410" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" /> </head>
Service Worker Basics
Registration
// lib/pwa.ts export async function registerServiceWorker() { if ('serviceWorker' in navigator) { try { const registration = await navigator.serviceWorker.register('/sw.js', { scope: '/', }); return registration; } catch (error) { console.error('SW registration failed:', error); } } } // Call on app mount useEffect(() => { registerServiceWorker(); }, []);
Basic Service Worker Structure
// public/sw.js const CACHE_NAME = 'myapp-v1'; const STATIC_ASSETS = ['/', '/offline', '/manifest.json']; // Install: Cache static assets self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)) ); self.skipWaiting(); }); // Activate: Clean old caches self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))) ) ); self.clients.claim(); }); // Fetch: Handle requests (see references for strategies) self.addEventListener('fetch', (event) => { event.respondWith(handleFetch(event.request)); });
See:
for caching strategy implementationsreferences/service-worker-patterns.md
Caching Strategies
| Strategy | Best For | Tradeoff |
|---|---|---|
| Cache-First | Static assets, fonts, images | Stale until cache updated |
| Network-First | API data, user content | Slower, needs connectivity |
| Stale-While-Revalidate | Balance freshness/speed | Background updates |
| Network-Only | Auth, real-time data | No offline support |
| Cache-Only | Versioned assets | Never updates |
See:
for full implementationsreferences/service-worker-patterns.md
Install Prompts
Handle the
beforeinstallprompt event to show a custom install UI:
// Basic pattern const [deferredPrompt, setDeferredPrompt] = useState(null); useEffect(() => { window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); setDeferredPrompt(e); }); }, []); const handleInstall = async () => { if (deferredPrompt) { deferredPrompt.prompt(); const { outcome } = await deferredPrompt.userChoice; // outcome: 'accepted' or 'dismissed' } };
See:
for fullreferences/install-prompt.mdhook and componentusePWAInstall
Offline Experience
Key patterns:
- Offline page fallback for navigation failures
hook to detect connectivityuseOnlineStatus- Offline banner to inform users
See:
for implementationsreferences/offline-handling.md
Background Sync
Queue actions while offline, execute when connectivity returns:
// In Service Worker self.addEventListener('sync', (event) => { if (event.tag === 'sync-data') { event.waitUntil(syncPendingData()); } }); // In App - trigger sync const registration = await navigator.serviceWorker.ready; await registration.sync.register('sync-data');
See:
for full IndexedDB integrationreferences/background-sync.md
Update Flow
Notify users when a new version is available:
// Basic pattern registration.addEventListener('updatefound', () => { const newWorker = registration.installing; newWorker?.addEventListener('statechange', () => { if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { // New version available - show update prompt } }); });
See:
forreferences/update-flow.mdhook and update strategiesusePWAUpdate
Next.js Integration
Options for Next.js PWA:
- next-pwa - Works with standard Next.js server
- Custom SW - Required for
(static sites)output: 'export' - Workbox CLI - Generate SW after build
See:
for detailed configurationsreferences/nextjs-integration.md
Quick Reference
| Task | Solution |
|---|---|
| Check if installed | |
| Force SW update | |
| Clear all caches | |
| Check online | |
| Get SW registration | |
| Skip waiting | in SW |
| Take control | in SW |
Testing PWA
Chrome DevTools
- Application tab → Manifest, Service Workers, Cache Storage
- Lighthouse → PWA audit
- Network → Offline checkbox to simulate
Debug Checklist
- Manifest loads (Application → Manifest)
- SW registered (Application → Service Workers)
- Cache populated (Application → Cache Storage)
- Install prompt fires (Console for beforeinstallprompt)
- Offline page works (Network → Offline)
- Update flow works (trigger update, verify prompt)
References
Detailed implementations in
/references/:
- Caching strategy implementationsservice-worker-patterns.md
-install-prompt.md
hook and install componentusePWAInstall
- Offline page, status hooks, bannersoffline-handling.md
- Background sync with IndexedDBbackground-sync.md
- Update detection and user promptsupdate-flow.md
- Next.js PWA configuration optionsnextjs-integration.md