progressive-web-app – Other skill for AI agents
Build Progressive Web Apps (PWAs) with offline support, installability, and caching strategies. Trigger whenever the user mentions PWA, service workers, web app manifests, Workbox, 'add to home screen', or wants their web app to work offline, feel native, or be installable.
Install the progressive-web-app skill with one command
Pick the tool you use, copy the command and paste it in your terminal. The skill lands in that tool's skills folder and is ready in your next session.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/progressive-web-app-4458d.tgz | tar xz -C ~/.claude/skills
Call it in Claude Code: /progressive-web-app – then describe the task you want done.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/progressive-web-app-4458d.tgz | tar xz -C ~/.codex/skills
Call it in Codex: $progressive-web-app – then describe the task you want done.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/progressive-web-app-4458d.tgz | tar xz -C ~/.hermes/skills
Call it in Hermes Agent: /progressive-web-app – then describe the task you want done.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/progressive-web-app-4458d.tgz | tar xz -C ~/.deepcode/skills
Call it in DeepSeek: /progressive-web-app – then describe the task you want done.
SKILL.md content
Progressive Web Apps (PWAs)
Overview
A Progressive Web App is a web application that uses modern browser capabilities to deliver a fast, reliable, and installable experience — even on unreliable networks. Common building blocks (install criteria vary by browser) are:
1. HTTPS — Required in production for service workers to register (localhost is exempt for development).
2. Web App Manifest (manifest.json) — Makes the app installable and defines its appearance on device home screens.
3. Service Worker (sw.js) — A background script that intercepts network requests, manages caches, and enables offline functionality.
When to Use This Skill
- Use when the user wants their web app to work offline or on unreliable networks.
- Use when building a mobile-first web project where users should be able to install the app to their home screen.
- Use when the user asks about caching strategies, service workers, or improving web app performance and resilience.
- Use when the user mentions Workbox, web app manifests, background sync, or push notifications for the web.
- Use when the user asks "can my website be installed like an app?" or "how do I make my site work offline?" — even if they don't use the word PWA.
Deliverables Checklist
This example layout uses the following files; these exact filenames are not a universal PWA requirement:
- [ ]
index.html— Links manifest, registers service worker - [ ]
manifest.json— Full app metadata and icon set - [ ]
sw.js— Service worker with install, activate, and fetch handlers - [ ]
app.js— Main app logic with SW registration and install prompt handling - [ ]
offline.html— Fallback page shown when navigation fails offline (required by this example's cache.addAll list, not by the PWA standard)
---
Step 1: Web App Manifest (manifest.json)
Defines how the app appears when installed. Must be linked from <head> via <link rel="manifest">.
{
"name": "My Awesome PWA",
"short_name": "MyPWA",
"description": "A fast, offline-capable Progressive Web App.",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait-primary",
"background_color": "#ffffff",
"theme_color": "#0055ff",
"icons": [
{
"src": "/assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
],
"screenshots": [
{
"src": "/assets/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
}
]
}Key fields:
display:standalonehides browser UI;minimal-uishows minimal controls;browseris standard tab.purpose: "maskable"on icons enables adaptive icons on Android (safe zone matters — keep content in center 80%).screenshotsis optional but required for Chrome's enhanced install dialog on desktop.
---
Step 2: HTML Shell (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Awesome PWA</title>
<!-- PWA manifest -->
<link rel="manifest" href="/manifest.json">
<!-- Theme color for browser chrome -->
<meta name="theme-color" content="#0055ff">
<!-- iOS-specific (Safari doesn't fully use manifest) -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="MyPWA">
<link rel="apple-touch-icon" href="/assets/icons/icon-192x192.png">
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<div id="app">
<header><h1>My PWA</h1></header>
<main id="content">Loading...</main>
<!-- Optional: install button, hidden by default -->
<button id="install-btn" hidden>Install App</button>
</div>
<script src="/app.js"></script>
</body>
</html>---
Step 3: Service Worker Registration & Install Prompt (app.js)
// ─── Service Worker Registration ───────────────────────────────────────────
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('[App] SW registered, scope:', registration.scope);
} catch (err) {
console.error('[App] SW registration failed:', err);
}
});
}
// ─── Install Prompt (Add to Home Screen) ───────────────────────────────────
let deferredPrompt;
const installBtn = document.getElementById('install-btn'); // may be null if omitted
// Capture the browser's install prompt — it fires before the browser's own UI
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault(); // Stop automatic mini-infobar on mobile
deferredPrompt = e;
if (installBtn) installBtn.hidden = false; // Show your custom install button
});
if (installBtn) {
installBtn.addEventListener('click', async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
console.log('[App] Install outcome:', outcome);
deferredPrompt = null;
installBtn.hidden = true;
});
}
// Fires when the app is installed (via browser or your button)
window.addEventListener('appinstalled', () => {
console.log('[App] PWA installed successfully');
if (installBtn) installBtn.hidden = true;
});---
Step 4: Service Worker (sw.js)
Cache Versioning (critical — always increment on deploy)
const CACHE_VERSION = 'v1';
const CACHE_PREFIX = 'my-pwa-'; // Unique to this app and deployment scope.
const STATIC_CACHE = `${CACHE_PREFIX}static-${CACHE_VERSION}`;
const DYNAMIC_CACHE = `${CACHE_PREFIX}dynamic-${CACHE_VERSION}`;
// Files to pre-cache during install (the "App Shell")
const APP_SHELL = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/assets/icons/icon-192x192.png',
'/offline.html', // Fallback page shown when network is unavailable
];The hand-written strategies are teaching sketches. Coordinate updates before skipWaiting/clients.claim to avoid old-page/new-worker incompatibility. Bound cache size/retention, preserve other apps' caches, and tie background revalidation to event.waitUntil with rejection handling.
Install — Pre-cache the App Shell
self.addEventListener('install', (event) => {
console.log('[SW] Installing...');
event.waitUntil(
caches.open(STATIC_CACHE).then((cache) => {
console.log('[SW] Pre-caching app shell');
return cache.addAll(APP_SHELL);
})
);
// Activate immediately without waiting for old SW to die
self.skipWaiting();
});Activate — Clean Up Old Caches
self.addEventListener('activate', (event) => {
console.log('[SW] Activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name.startsWith(CACHE_PREFIX) && name !== STATIC_CACHE && name !== DYNAMIC_CACHE)
.map((name) => {
console.log('[SW] Deleting old cache:', name);
return caches.delete(name);
})
);
})
);
// Take control of all pages immediately
self.clients.claim();
});Fetch — Caching Strategies
Choose the right strategy per resource type:
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Only handle GET requests from our own origin
if (request.method !== 'GET' || url.origin !== location.origin) return;
// Explicit public resources only. Never cache authenticated/user-specific
// HTML or API responses by URL; cache contents can survive logout/account changes.
if (!APP_SHELL.includes(url.pathname)) return;
// Strategy A: Cache-First (for static assets — fast, tolerates stale)
if (url.pathname.match(/\.(css|js|png|jpg|svg|woff2)$/)) {
event.respondWith(cacheFirst(request));
return;
}
// Strategy B: Network-First (for HTML pages — fresh, falls back to cache)
if (request.headers.get('Accept')?.includes('text/html')) {
event.respondWith(networkFirst(request));
return;
}
// API caching is intentionally omitted; default to network-only for private data.
});
// ─── Strategy Implementations ──────────────────────────────────────────────
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(STATIC_CACHE);
if (response.ok && response.type !== "opaque") await cache.put(request, response.clone());
return response;
} catch {
// Nothing useful to fall back to for assets
return new Response('Asset unavailable of… (full content is in the download package)
Related skills
- project-brief – Project Detail Optimizer. Use when working with one sentence, rough notes or an existing…
- projection-patterns – Build read models and projections from event streams. Use when implementing CQRS read…
- projects-work-management – Project register: owner, team, priority, progress percentage, milestones, deliverables…
- prometheus-grafana – Set up metrics collection and visualization with Prometheus and Grafana.
- promotion-upgrade-requests – Promotion register: current and requested role and grade, justification, OKR and…
- protocol-reverse – Authorized reverse engineering of custom binary protocols, Protobuf/gRPC schemas…
- public-relations – When the user wants help with public relations, earned media, press coverage, journalist…
- purchase-accounting – Purchase register: supplier, invoice, gross amount, VAT and TDS, net payable, ledger…