skills/pwa-expert/SKILL.md
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", "Service Worker", "offline", "install prompt", "beforeinstallprompt", "manifest.json", "workbox", "cache-first". NOT for native app development (use React Native), general web performance (use performance docs), or server-side rendering.
npx skillsauth add curiositech/windags-skills pwa-expertInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build installable, offline-capable web apps with Service Workers, smart caching, and native-like experiences.
REQUEST TYPE?
├─ Static assets (CSS, JS, fonts, images)
│ └─ CACHE-FIRST: Check cache → fallback to network
├─ API data that changes frequently
│ ├─ User expects fresh data?
│ │ └─ NETWORK-FIRST: Try network → fallback to cache
│ └─ Performance over freshness?
│ └─ STALE-WHILE-REVALIDATE: Serve cache → update in background
└─ Authentication/Real-time data
└─ NETWORK-ONLY: Always fetch, no cache
CONNECTIVITY STATUS?
├─ navigator.onLine === false
│ └─ Serve from cache OR show offline page
├─ Request fails (fetch throws)
│ ├─ Resource in cache?
│ │ └─ Serve cached version
│ └─ No cache available?
│ └─ Show offline fallback
└─ Online AND request succeeds
└─ Update cache with fresh data
beforeinstallprompt FIRED?
├─ User just arrived (< 30 seconds on site)?
│ └─ DEFER: Store prompt, don't show yet
├─ User engaged (scrolled, clicked, 2+ page views)?
│ ├─ Mobile device?
│ │ └─ SHOW: Mobile users expect app install
│ └─ Desktop?
│ └─ CONTEXTUAL: Show near task completion
└─ User dismissed before?
└─ WAIT: Don't show again for 7+ days
Symptom: Users see outdated content, complain "app isn't working"
Detection: User reports + cache timestamp > expected refresh interval
Fix: Force cache update with caches.delete() + trigger fresh fetch
Symptom: PWA features not working, install prompt never appears
Detection: Console error "Failed to register service worker" OR navigator.serviceWorker undefined
Fix: Check HTTPS requirement (HTTP only works on localhost), validate SW file path, verify manifest linked in HTML
Symptom: App updates not appearing, old SW keeps running
Detection: registration.waiting exists but skipWaiting() not called
Fix: Implement update flow with postMessage() to SW + skipWaiting() + clients.claim()
Symptom: "Add to Home Screen" never appears, PWA audit fails Detection: DevTools Application tab shows manifest errors OR Lighthouse PWA score < 80 Fix: Validate required fields (name, start_url, display, icons 192x192 + 512x512), ensure HTTPS, check icon paths exist
Symptom: App slows down, storage quota exceeded errors
Detection: Cache storage > 50MB OR "QuotaExceededError" in console
Fix: Implement cache expiration (maxEntries, maxAgeSeconds), clean old cache versions in SW activate event
Scenario: Convert existing Next.js recovery support app to installable PWA with offline meeting finder
Step 1: Assess Current State
Step 2: Decision - Caching Strategy
Meeting data API → Changes daily → NETWORK-FIRST
User profile images → Rarely change → CACHE-FIRST
Static assets → Never change per version → CACHE-FIRST
Meeting search → Mix strategy → STALE-WHILE-REVALIDATE
Step 3: Create manifest.json
{
"name": "Recovery Meetings Finder",
"short_name": "Meetings",
"start_url": "/",
"display": "standalone",
"background_color": "#1a1410",
"theme_color": "#1a1410",
"icons": [
{"src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png"},
{"src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png"}
]
}
Step 4: Implement Service Worker
// public/sw.js - Network-first for API
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/meetings')) {
event.respondWith(networkFirst(event.request));
}
});
Expert catches: Novice would miss offline fallback page, expert adds catch-all route to serve cached "/offline" for navigation requests that fail.
Do NOT use this skill for:
react-native-expert or flutter-expert insteadweb-performance-expert for non-PWA performance issuesnextjs-expert or framework-specific skillspush-notification-expert for complex notification systemsWhen to delegate:
data-sync-expertcdn-expertcapacitor-expert or electron-expertdata-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.