Créer une PWA avec Claude Code : manifest, Service Worker et offline
Guide pratique pour créer une PWA avec Claude Code : manifest, icônes, Service Worker, fallback offline, cache et validation.
Une PWA, ou Progressive Web App, est une application web qui peut se rapprocher d’une application installee : nom et icone, lancement en mode standalone, ressources en cache, et affichage d’une page utile quand la connexion tombe.
Le piege classique consiste a croire que le manifest.webmanifest suffit. En pratique, une PWA fiable demande aussi des icones valides, un Service Worker, une page offline, une strategie de cache, une verification de l’installabilite et des controles dans Chrome DevTools et Lighthouse. Une icone en 404 ou un HTML trop longtemps mis en cache peut creer un bug visible seulement apres deploiement.
Ce guide montre comment demander a Claude Code une implementation PWA propre et verifiable. Si vous debutez avec l’outil, lisez d’abord le guide de demarrage Claude Code. Pour les sources officielles, gardez sous la main web.dev Learn PWA, le guide MDN sur les PWA installables, les bonnes pratiques PWA de MDN, la note Chrome sur les criteres d’installation et la documentation Claude Code.
Vue d’ensemble
Une PWA est une chaine de fichiers. Le HTML reference le manifest, l’application enregistre le Service Worker, le Service Worker controle les requetes dans son scope et choisit entre reseau, cache ou fallback offline.
L'utilisateur ouvre le site
-> index.html reference manifest.webmanifest
-> register-sw.js enregistre /sw.js
-> sw.js precache l'app shell
-> fetch applique une strategie par type de ressource
-> une navigation offline recoit offline.html
Avant de modifier le code, clarifiez trois choix.
| Choix | Exemple | Risque |
|---|---|---|
| URL de demarrage et scope | / ou /app/ | Le Service Worker ne controle pas les bonnes pages |
| Ressources en cache | HTML, CSS, JS, images, offline.html | Anciennes versions ou 404 persistants |
| Comportement offline | Page offline, derniere page, erreur API | L’utilisateur ne comprend pas l’etat |
Pour un site de contenu, une bibliotheque de cours ou un petit dashboard, commencez simplement : Network First pour les navigations HTML, Cache First pour les images, Stale While Revalidate pour CSS, JS et polices. Pour aller plus loin, reliez ce travail a l’article sur les strategies de cache avec Claude Code.
Prompt pour Claude Code
Un prompt precis donne de meilleurs resultats qu’une demande vague du type “rends cette app PWA”.
Transforme cette application Vite/React existante en PWA.
Exigences:
- Ajouter public/manifest.webmanifest
- Referencer des icones PNG 192x192, 512x512 et maskable 512x512
- Ajouter public/offline.html
- Ajouter public/sw.js comme Service Worker
- Enregistrer le Service Worker depuis src/register-sw.js
- Utiliser Network First pour les navigations HTML
- Utiliser Cache First pour les images
- Utiliser Stale While Revalidate pour CSS, JS et fonts
- Ne pas mettre en cache les POST ni les requetes cross-origin
- Afficher un message quand une nouvelle version est disponible
- Terminer par une checklist DevTools et Lighthouse
Contraintes:
- Ne pas ajouter un fetch handler vide juste pour l'installabilite
- Expliquer chaque fichier modifie
- Signaler les chemins dependants du base path de production
Masa a constate sur une petite landing de formation que la generation de code etait rapide, mais que le premier vrai bug venait d’un decalage entre start_url et scope. Demander a Claude Code d’expliciter ces hypotheses evite des corrections tardives.
Manifest et icones
Ajoutez public/manifest.webmanifest. name est le nom complet, short_name le nom court, start_url la page d’ouverture, et scope le perimetre controle par l’application.
{
"id": "/",
"name": "ClaudeCodeLab PWA Demo",
"short_name": "CCLab",
"description": "Demo PWA offline construite avec Claude Code",
"start_url": "/?source=pwa",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0f766e",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}
Puis referencez-le dans le head.
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0f766e" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
Les icones doivent etre de vrais PNG. Prevoyez au minimum 192x192 et 512x512, plus une icone maskable 512x512 avec une marge autour du logo. Apres la modification, ouvrez chaque URL d’icone dans le navigateur pour verifier le statut 200.
Service Worker
Placez ce fichier dans public/sw.js. Il precache l’app shell, supprime les vieux caches et limite son action aux requetes GET du meme origin.
const VERSION = "2026-06-02";
const STATIC_CACHE = `static-${VERSION}`;
const RUNTIME_CACHE = `runtime-${VERSION}`;
const APP_SHELL = [
"/",
"/offline.html",
"/manifest.webmanifest",
"/icons/icon-192.png",
"/icons/icon-512.png",
"/icons/icon-maskable-512.png"
];
self.addEventListener("install", (event) => {
event.waitUntil(
caches
.open(STATIC_CACHE)
.then((cache) => cache.addAll(APP_SHELL))
.then(() => self.skipWaiting())
);
});
self.addEventListener("activate", (event) => {
const allowedCaches = [STATIC_CACHE, RUNTIME_CACHE];
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(
keys
.filter((key) => !allowedCaches.includes(key))
.map((key) => caches.delete(key))
)
)
.then(() => self.clients.claim())
);
});
self.addEventListener("fetch", (event) => {
const { request } = event;
if (request.method !== "GET") return;
const url = new URL(request.url);
if (url.origin !== self.location.origin) return;
if (request.mode === "navigate") {
event.respondWith(networkFirstPage(request));
return;
}
if (request.destination === "image") {
event.respondWith(cacheFirst(request));
return;
}
if (["style", "script", "font"].includes(request.destination)) {
event.respondWith(staleWhileRevalidate(request));
}
});
async function networkFirstPage(request) {
const cache = await caches.open(RUNTIME_CACHE);
try {
const response = await fetch(request);
if (response.ok) await cache.put(request, response.clone());
return response;
} catch {
const cached = await cache.match(request);
return cached || (await caches.match("/offline.html")) || new Response("Offline", { status: 503 });
}
}
async function cacheFirst(request) {
const cached = await caches.match(request);
if (cached) return cached;
const response = await fetch(request);
if (response.ok) {
const cache = await caches.open(RUNTIME_CACHE);
await cache.put(request, response.clone());
}
return response;
}
async function staleWhileRevalidate(request) {
const cache = await caches.open(RUNTIME_CACHE);
const cached = await cache.match(request);
const networkPromise = fetch(request)
.then((response) => {
if (response.ok) cache.put(request, response.clone());
return response;
})
.catch(() => undefined);
if (cached) return cached;
return (await networkPromise) || new Response("Network error", { status: 504 });
}
Ce choix est volontairement prudent. Les reponses personnalisees, les paiements, les formulaires et les permissions ne sont pas mis en cache. Un Service Worker peut ameliorer la vitesse, mais il peut aussi conserver trop longtemps une mauvaise reponse.
Page offline et enregistrement
public/offline.html doit rester autonome.
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hors ligne</title>
</head>
<body>
<main>
<h1>Vous etes hors ligne</h1>
<p>Rechargez la page lorsque la connexion revient. Certaines pages recentes peuvent rester disponibles.</p>
<p><a href="/">Retour a l'accueil</a></p>
</main>
</body>
</html>
Enregistrez ensuite le worker dans src/register-sw.js.
export async function registerServiceWorker() {
if (!("serviceWorker" in navigator)) return;
window.addEventListener("load", async () => {
try {
const registration = await navigator.serviceWorker.register("/sw.js", {
scope: "/"
});
registration.addEventListener("updatefound", () => {
const worker = registration.installing;
if (!worker) return;
worker.addEventListener("statechange", () => {
if (worker.state === "installed" && navigator.serviceWorker.controller) {
document.querySelector("[data-refresh-app]")?.removeAttribute("hidden");
}
});
});
} catch (error) {
console.error("Service Worker registration failed:", error);
}
});
}
Appelez-le une seule fois.
import { registerServiceWorker } from "./register-sw.js";
registerServiceWorker();
Le message de mise a jour evite les melanges entre ancien HTML et nouveau JavaScript. C’est une cause frequente de bugs apres deploiement.
Installation et validation
Sur certains navigateurs Chromium, beforeinstallprompt est declenche lorsque l’application est installable. Il faut l’utiliser comme amelioration progressive.
let deferredPrompt = null;
window.addEventListener("beforeinstallprompt", (event) => {
event.preventDefault();
deferredPrompt = event;
document.querySelector("[data-install-app]")?.removeAttribute("hidden");
});
document.querySelector("[data-install-app]")?.addEventListener("click", async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const choice = await deferredPrompt.userChoice;
console.info("Install result:", choice.outcome);
deferredPrompt = null;
});
Pour valider, utilisez DevTools Application pour le manifest, le Service Worker, Cache Storage et le mode offline. Lighthouse reste utile pour performance, accessibilite, bonnes pratiques et SEO.
npm run build
npx serve dist -l 4173
npx lighthouse http://localhost:4173 --view --only-categories=performance,accessibility,best-practices,seo
| Controle | Emplacement | Critere |
|---|---|---|
| Manifest | Application > Manifest | nom, start_url et icones sans erreur |
| Service Worker | Application > Service Workers | /sw.js est activated |
| Offline | Network Offline puis reload | offline.html ou page recente apparait |
| Cache Storage | Application > Cache Storage | caches static/runtime attendus |
| Lighthouse | Rapport Lighthouse | pas de regression performance, SEO, accessibilite |
Cas d’usage, CTA et pieges
Une PWA est pertinente pour les usages repetes : bibliotheque de cours consultee dans les transports, dashboard interne ouvert chaque jour, guide d’evenement dans un lieu au reseau instable, ou parcours e-commerce qui profite d’images deja chargees.
La monetisation doit parler de continuite, pas seulement d’installation. Mesurez les clics d’installation, les retours, les lectures terminees, les affichages offline et les clics vers l’offre. Pour des modeles et prompts Claude Code, consultez la bibliotheque de produits. En equipe, livrez aussi la revue de cache, la validation du deploiement et les evenements analytics.
Les pieges recurrent sont simples : scope et start_url incoherents, HTML en Cache First, donnees privees en cache, icones en 404, et ancien Service Worker non supprime pendant le debug. En cas de doute, Unregister, videz Cache Storage et recommencez le premier chargement.
Resultat teste
Lors d’un test de Masa sur une petite landing Vite de formation, Claude Code a ajoute rapidement le manifest, la page offline et le code d’enregistrement. Le vrai temps a ete consacre a verifier les URLs d’icones, le rechargement en mode Offline et le nettoyage des anciens caches apres deploiement. La bonne approche consiste a livrer d’abord un fallback offline minimal, puis a elargir le cache uniquement quand l’experience recurrente y gagne vraiment.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.