Use Cases (Aktualisiert: 2.6.2026)

PWA mit Claude Code bauen: Manifest, Service Worker und Offline

Praxisleitfaden fur eine PWA mit Claude Code: Manifest, Icons, Service Worker, Offline-Fallback, Cache und Validierung.

PWA mit Claude Code bauen: Manifest, Service Worker und Offline

Eine PWA, also Progressive Web App, bringt eine Webanwendung naher an das Verhalten einer installierten App: Name und Icon, Start in einem eigenen Fenster, gezieltes Caching und eine sinnvolle Offline-Seite statt eines Browserfehlers.

Der haufige Fehler ist, nur ein manifest.webmanifest anzulegen und die Arbeit als erledigt zu betrachten. Eine belastbare PWA braucht auch korrekte Icons, einen Service Worker, eine Offline-Fallback-Seite, eine Cache-Strategie, Installierbarkeitschecks sowie Validierung in Chrome DevTools und Lighthouse. Ein 404 bei einem Icon oder ein veraltetes HTML im Cache reicht fur schwer nachvollziehbare Produktionsfehler.

Dieser Leitfaden zeigt, wie du Claude Code fur eine PWA-Implementierung einsetzt, ohne die Kontrolle uber Pfade, Cache und Tests zu verlieren. Wenn du Claude Code noch nicht regelmassig nutzt, lies zuerst den Claude Code Einstieg. Als offizielle Referenzen dienen web.dev Learn PWA, MDN zu installierbaren PWAs, MDN PWA Best Practices, Chrome zu aktualisierten Installationskriterien und die Claude Code Dokumentation.

Grundstruktur

Eine PWA besteht aus mehreren verbundenen Teilen. Das HTML verweist auf das Manifest, der App-Einstieg registriert den Service Worker, und der Service Worker entscheidet fur kontrollierte Requests, ob Netzwerk, Cache oder Offline-Fallback genutzt wird.

Nutzer offnet die Seite
  -> index.html bindet manifest.webmanifest ein
  -> register-sw.js registriert /sw.js
  -> sw.js precacht die App Shell
  -> fetch wahlt Strategien nach Ressourcentyp
  -> Offline-Navigation erhalt offline.html

Vor der Umsetzung brauchst du drei Entscheidungen.

EntscheidungBeispielRisiko
Start-URL und scope/ oder /app/Der Service Worker kontrolliert falsche Seiten
Cache-InhalteHTML, CSS, JS, Bilder, offline.htmlAlte Dateien oder 404 bleiben bestehen
Offline-VerhaltenOffline-Seite, letzte Seite, API-FehlerNutzer verstehen den Zustand nicht

Fur Blogs, Kursseiten und kleine Dashboards ist ein konservativer Start sinnvoll: Network First fur HTML-Navigationen, Cache First fur Bilder und Icons, Stale While Revalidate fur CSS, JavaScript und Fonts. Fur mehr Tiefe passt der Artikel zu Caching-Strategien mit Claude Code.

Prompt fur Claude Code

Claude Code liefert bessere Ergebnisse, wenn Dateien, Strategien und Prufpunkte klar genannt werden.

Wandle diese bestehende Vite/React-App in eine PWA um.

Anforderungen:
- public/manifest.webmanifest hinzufugen
- PNG-Icons 192x192, 512x512 und maskable 512x512 referenzieren
- public/offline.html hinzufugen
- public/sw.js als Service Worker hinzufugen
- Service Worker aus src/register-sw.js registrieren
- Network First fur HTML-Navigationen
- Cache First fur Bilder
- Stale While Revalidate fur CSS, JS und Fonts
- Keine POST- oder Cross-Origin-Requests cachen
- Hinweis anzeigen, wenn eine neue Service-Worker-Version verfugbar ist
- Am Ende DevTools- und Lighthouse-Checklist ausgeben

Einschrankungen:
- Kein leerer fetch handler nur fur Installierbarkeit
- Jede geanderte Datei erklaren
- Pfade markieren, die vom Produktions-Base-Path abhangen

Bei Masas Test mit einer kleinen Kurs-Landingpage war die Codegenerierung nicht das Problem. Der erste echte Fehler war ein Mismatch zwischen start_url und scope. Genau solche Annahmen sollte Claude Code vor dem Editieren sichtbar machen.

Manifest und Icons

Lege public/manifest.webmanifest an. name ist der vollstandige App-Name, short_name die kurze Variante, start_url die Startseite, scope der kontrollierte URL-Bereich.

{
  "id": "/",
  "name": "ClaudeCodeLab PWA Demo",
  "short_name": "CCLab",
  "description": "Offline-fahige PWA-Demo mit 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"
    }
  ]
}

Im HTML-Head wird das Manifest eingebunden.

<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0f766e" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />

Nutze echte PNG-Dateien. Mindestens 192x192 und 512x512 sind Pflicht fur eine ordentliche Anzeige, ein maskable 512x512 Icon mit sicherem Innenabstand verhindert unschone Zuschnitte. Offne jede Icon-URL nach dem Editieren direkt im Browser.

Service Worker

public/sw.js ist der zentrale Teil. Dieses Beispiel precacht die App Shell, entfernt alte Caches und verarbeitet nur GET-Requests vom gleichen 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 });
}

Wichtig ist die Zuruckhaltung: keine POSTs, keine externen Origins, keine privaten API-Antworten. Authentifizierung, Zahlung, Warenkorb, Rechte und Lagerbestand gehoren nicht in einen allgemeinen Runtime-Cache.

Offline-Seite und Registrierung

public/offline.html sollte ohne externe Abhangigkeiten funktionieren.

<!doctype html>
<html lang="de">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Offline</title>
  </head>
  <body>
    <main>
      <h1>Du bist offline</h1>
      <p>Lade neu, wenn die Verbindung zuruck ist. Kurzlich geoffnete Seiten konnen weiter verfugbar sein.</p>
      <p><a href="/">Zur Startseite</a></p>
    </main>
  </body>
</html>

Registriere den Worker aus 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);
    }
  });
}

Einmal im Einstieg importieren:

import { registerServiceWorker } from "./register-sw.js";

registerServiceWorker();

Der Update-Hinweis ist kein Luxus. Service Worker haben einen eigenen Lifecycle, und eine neue Version kann warten, wahrend alte Tabs noch vom alten Worker kontrolliert werden.

Installation und Validierung

Chromium-basierte Browser konnen beforeinstallprompt auslosen, wenn die App installierbar ist. Behandle dieses Event als progressive Verbesserung.

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;
});

Fur die Validierung ist DevTools Application entscheidend: Manifest, Service Worker, Cache Storage und Offline-Verhalten. Lighthouse nutzt du zusatzlich fur Performance, Accessibility, Best Practices und SEO.

npm run build
npx serve dist -l 4173
npx lighthouse http://localhost:4173 --view --only-categories=performance,accessibility,best-practices,seo
CheckOrtKriterium
ManifestApplication > ManifestName, start_url und Icons ohne Fehler
Service WorkerApplication > Service Workers/sw.js ist activated
OfflineNetwork Offline und Reloadoffline.html oder letzte Seite erscheint
Cache StorageApplication > Cache Storagestatic/runtime Caches passen
LighthouseReportkeine Regression bei Performance, SEO, Accessibility

Use Cases, CTA und Fallen

PWA lohnt sich bei wiederkehrender Nutzung: Kursbibliotheken fur unterwegs, interne Dashboards, Event-Guides mit schwachem Netz oder Commerce-Seiten mit vielen Bildern. Monetarisierung entsteht nicht durch “installierbar”, sondern durch bessere Wiederkehr, messbare CTAs und weniger Abbruch.

Fur ClaudeCodeLab heisst das: Install-Klicks, Offline-Fallback-Aufrufe, abgeschlossene Lesevorgange und Produkt-CTAs messen. Vorlagen und Claude-Code-Prompt-Packs findest du in der Produktbibliothek. Bei Teams sollte die Lieferung Cache-Review, Deployment-Pfade und Analytics-Events enthalten.

Typische Fallen sind scope/start_url-Mismatch, HTML mit Cache First, private API-Daten im Cache, Icon-404 und Debugging mit altem Service Worker. Bei unklaren Fehlern immer Unregister, Cache Storage leeren und den Erstbesuch erneut testen.

Getestetes Ergebnis

In Masas Test mit einer kleinen Vite-Kurs-Landingpage erzeugte Claude Code Manifest, Offline-Seite und Registrierung schnell. Der grosste Aufwand lag in der Kontrolle der Icon-URLs, im Offline-Reload und im Loschen alter Caches nach einem neuen Deploy. Die sichere Reihenfolge ist: erst ein minimaler Offline-Fallback, dann gezieltes Caching fur Ressourcen, die wiederkehrenden Nutzern wirklich helfen.

#Claude Code #PWA #Service Worker #offline #mobile
Kostenlos

Kostenloses PDF: Claude-Code-Cheatsheet

E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.

Wir schützen Ihre Daten und senden keinen Spam.

Masa

Über den Autor

Masa

Engineer für praktische Claude-Code-Workflows und Team-Einführung.