Progressive Web Apps mit Claude Code: Offline-First 2026

Progressive Web Apps bringen native App-Erlebnisse ins Browser-Ökosystem — Offline-Fähigkeit, Push Notifications und Installation vom Homescreen. Claude Code implementiert alle PWA-Bausteine: Service Worker, Web App Manifest, Caching-Strategien, Background Sync und Push Notifications.

PWA Grundlagen: Manifest und Service Worker

ManifestWeb App Manifest — Die Visitenkarte deiner PWA

# Prompt: "Erstelle ein vollständiges Web App Manifest für meine PWA" // public/manifest.json { "name": "MeinApp — Produktivität ohne Grenzen", "short_name": "MeinApp", "description": "Produktivitäts-App mit Offline-Unterstützung", "start_url": "/", "display": "standalone", // Kein Browser-Chrome! "orientation": "portrait", "theme_color": "#5a0fc8", "background_color": "#ffffff", "scope": "/", "icons": [ { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" } ], "screenshots": [ { "src": "/screenshots/desktop.png", "sizes": "1280x800", "form_factor": "wide" }, { "src": "/screenshots/mobile.png", "sizes": "390x844", "form_factor": "narrow" } ], "shortcuts": [ { "name": "Neue Aufgabe", "url": "/tasks/new", "icons": [{ "src": "/icons/add.png", "sizes": "96x96" }] } ] } // HTML <head> — Manifest verknüpfen: <link rel="manifest" href="/manifest.json"> <meta name="theme-color" content="#5a0fc8"> <meta name="apple-mobile-web-app-capable" content="yes"> <link rel="apple-touch-icon" href="/icons/icon-192.png">
Claude Code Tipp: "Generiere Icons in allen PWA-Größen (72, 96, 128, 144, 152, 192, 384, 512px) aus meinem SVG-Logo und lege sie in public/icons/ ab." Claude Code nutzt sharp oder Jimp und generiert alle Größen automatisch — inklusive maskable-Versionen mit Padding für Android Adaptive Icons.

OfflineService Worker Grundgerüst

# Prompt: "Erstelle einen Service Worker mit Install, Activate und Fetch Events" // public/sw.js const CACHE_NAME = 'meinapp-v1'; const STATIC_ASSETS = [ '/', '/index.html', '/offline.html', '/assets/css/style.css', '/assets/js/app.js' ]; // Install: Statische Assets precachen self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(STATIC_ASSETS)) .then(() => self.skipWaiting()) // Sofort aktiv werden ); }); // Activate: Alte Caches löschen self.addEventListener('activate', event => { event.waitUntil( caches.keys().then(keys => Promise.all( keys .filter(key => key !== CACHE_NAME) .map(key => caches.delete(key)) ) ).then(() => self.clients.claim()) ); }); // Service Worker im HTML registrieren: if ('serviceWorker' in navigator) { navigator.serviceWorker .register('/sw.js') .then(reg => console.log('SW registriert:', reg.scope)) .catch(err => console.error('SW Fehler:', err)); }

Caching-Strategien: Cache First, Network First, Stale-While-Revalidate

CacheDie drei wichtigsten Strategien

# Prompt: "Implementiere alle drei Haupt-Caching-Strategien in meinem Service Worker" // --- 1. CACHE FIRST (statische Assets) --- // Ideal für: CSS, JS, Bilder, Fonts — ändert sich selten async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; const response = await fetch(request); const cache = await caches.open(CACHE_NAME); cache.put(request, response.clone()); return response; } // --- 2. NETWORK FIRST (API-Daten) --- // Ideal für: REST-Endpoints, dynamische Inhalte async function networkFirst(request) { try { const response = await fetch(request); const cache = await caches.open('api-cache'); cache.put(request, response.clone()); return response; } catch { const cached = await caches.match(request); return cached || new Response('{"error":"offline"}', { headers: { 'Content-Type': 'application/json' } }); } } // --- 3. STALE-WHILE-REVALIDATE (News, Feeds) --- // Ideal für: Blog-Posts, Nachrichten — schnell laden + im Hintergrund aktualisieren async function staleWhileRevalidate(request) { const cache = await caches.open('content-cache'); const cached = await cache.match(request); const networkPromise = fetch(request).then(res => { cache.put(request, res.clone()); return res; }); return cached || networkPromise; // Cache sofort, Update im Hintergrund } // Strategie nach URL-Muster routing: self.addEventListener('fetch', event => { const { url } = event.request; if (url.includes('/api/')) event.respondWith(networkFirst(event.request)); else if (url.includes('/blog/')) event.respondWith(staleWhileRevalidate(event.request)); else event.respondWith(cacheFirst(event.request)); });
Strategie-Auswahl: Cache First für unveränderliche Assets (fingerprinted), Network First für frische Daten, Stale-While-Revalidate für Content der sich gelegentlich ändert. Claude Code wählt automatisch die richtige Strategie wenn ihr den Inhaltstyp beschreibt.

Push Notifications mit der Web Push API

PushWeb Push — Vollständige Implementierung

# Prompt: "Implementiere Web Push Notifications mit VAPID-Authentifizierung" // 1. VAPID Keys generieren (einmalig auf dem Server): // npm install web-push const webpush = require('web-push'); const keys = webpush.generateVAPIDKeys(); // → publicKey + privateKey in .env speichern! // 2. Frontend — Push-Subscription erstellen: async function subscribeToPush() { const permission = await Notification.requestPermission(); if (permission !== 'granted') return; const reg = await navigator.serviceWorker.ready; const subscription = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) }); // Subscription ans Backend senden: await fetch('/api/push/subscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); } // 3. Service Worker — Push empfangen + anzeigen: self.addEventListener('push', event => { const data = event.data?.json() || { title: 'Neue Nachricht', body: '' }; event.waitUntil( self.registration.showNotification(data.title, { body: data.body, icon: '/icons/icon-192.png', badge: '/icons/badge-96.png', tag: data.tag || 'default', data: { url: data.url || '/' }, actions: [ { action: 'open', title: 'Öffnen' }, { action: 'dismiss', title: 'Schließen' } ] }) ); }); // Notification Klick → App öffnen: self.addEventListener('notificationclick', event => { event.notification.close(); if (event.action === 'open' || !event.action) { event.waitUntil(clients.openWindow(event.notification.data.url)); } }); // 4. Backend — Notification versenden (Node.js): webpush.setVapidDetails('mailto:admin@example.com', VAPID_PUBLIC, VAPID_PRIVATE); await webpush.sendNotification(subscription, JSON.stringify({ title: '🚀 Neue Funktion verfügbar', body: 'Schau dir das neue Dashboard an!', url: '/dashboard' }));

Background Sync für Offline-Aktionen

SyncBackground Sync — Aktionen offline zwischenspeichern

# Prompt: "Implementiere Background Sync für Offline-Formularübermittlung" // Frontend — Sync registrieren wenn offline: async function submitFormWithSync(formData) { if (navigator.onLine) { return fetch('/api/tasks', { method: 'POST', body: JSON.stringify(formData) }); } // Offline: Daten in IndexedDB speichern await saveToIDB('pending-tasks', { id: Date.now(), ...formData }); // Background Sync registrieren: const reg = await navigator.serviceWorker.ready; await reg.sync.register('sync-tasks'); showToast('Gespeichert — wird synchronisiert sobald online'); } // Service Worker — Sync Event abarbeiten: self.addEventListener('sync', event => { if (event.tag === 'sync-tasks') { event.waitUntil(syncPendingTasks()); } }); async function syncPendingTasks() { const tasks = await getFromIDB('pending-tasks'); for (const task of tasks) { const res = await fetch('/api/tasks', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(task) }); if (res.ok) await deleteFromIDB('pending-tasks', task.id); } } // Periodic Background Sync (experimentell — Chrome 80+): const reg = await navigator.serviceWorker.ready; if ('periodicSync' in reg) { await reg.periodicSync.register('refresh-feed', { minInterval: 24 * 60 * 60 * 1000 }); }
IndexedDB Helper: "Erstelle einen einfachen IndexedDB Wrapper mit get, set, delete und getAll für die Offline-Queue." Claude Code generiert einen sauberen Promise-basierten Wrapper — alternativ empfiehlt es die idb-Bibliothek von Jake Archibald für typsicheren IDB-Zugriff.

PWA in Next.js mit next-pwa

Next.jsnext-pwa Integration — Zero Config PWA

# Prompt: "Füge PWA-Support zu meiner Next.js App hinzu mit next-pwa" // 1. Installation: npm install next-pwa // 2. next.config.js: const withPWA = require('next-pwa')({ dest: 'public', register: true, skipWaiting: true, disable: process.env.NODE_ENV === 'development', runtimeCaching: [ { urlPattern: /^https:\/\/api\.example\.com\/.*/i, handler: 'NetworkFirst', options: { cacheName: 'api-cache', expiration: { maxEntries: 200, maxAgeSeconds: 24 * 60 * 60 } } }, { urlPattern: /\.(png|jpg|jpeg|svg|gif|webp)$/, handler: 'CacheFirst', options: { cacheName: 'image-cache', expiration: { maxEntries: 100, maxAgeSeconds: 30 * 24 * 60 * 60 } } }, { urlPattern: /\/_next\/static\/.*/i, handler: 'CacheFirst', options: { cacheName: 'next-static', expiration: { maxEntries: 300 } } } ] }); module.exports = withPWA({ reactStrictMode: true, }); // 3. pages/_app.tsx — Offline-Status anzeigen: import { useEffect, useState } from 'react'; export default function App({ Component, pageProps }) { const [isOnline, setIsOnline] = useState(true); useEffect(() => { const update = () => setIsOnline(navigator.onLine); window.addEventListener('online', update); window.addEventListener('offline', update); return () => { window.removeEventListener('online', update); window.removeEventListener('offline', update); }; }, []); return ( <> {!isOnline && <div className="offline-banner">Du bist offline — Daten werden synchronisiert sobald du wieder verbunden bist.</div>} <Component {...pageProps} /> </> ); }

App RouterNext.js 14+ App Router PWA

# Prompt: "Konfiguriere PWA für Next.js App Router mit Metadata API" // app/layout.tsx — Manifest via Metadata API: import type { Metadata } from 'next'; export const metadata: Metadata = { manifest: '/manifest.json', appleWebApp: { capable: true, statusBarStyle: 'default', title: 'MeinApp', }, formatDetection: { telephone: false }, themeColor: [ { media: '(prefers-color-scheme: light)', color: '#5a0fc8' }, { media: '(prefers-color-scheme: dark)', color: '#7c3aed' } ] }; // app/manifest.ts — Dynamisches Manifest (Next.js 14+): import type { MetadataRoute } from 'next'; export default function manifest(): MetadataRoute.Manifest { return { name: 'MeinApp', short_name: 'MeinApp', display: 'standalone', background_color: '#ffffff', theme_color: '#5a0fc8', icons: [ { src: '/icons/icon-192.png', sizes: '192x192', type: 'image/png' }, { src: '/icons/icon-512.png', sizes: '512x512', type: 'image/png' } ] }; }
App Router Hinweis: next-pwa hat noch keine offizielle App-Router-Unterstützung (Stand Mai 2026). Alternativen: @ducanh2912/next-pwa (aktiv gepflegt) oder manueller Service Worker in public/sw.js mit next-pwa@5.x. Claude Code kennt beide Ansätze und empfiehlt je nach Projektanforderungen.

Lighthouse PWA Score optimieren

Lighthouse100/100 PWA Score — Checkliste

# Prompt: "Analysiere meinen Lighthouse PWA Score und fixe alle Probleme" // ✅ PFLICHT für PWA Installierbarkeit: // 1. manifest.json mit name, short_name, icons (192px + 512px maskable) // 2. Service Worker registriert + aktiv // 3. HTTPS (localhost zählt für Dev) // 4. start_url im Cache vorhanden (Offline-Start möglich) // 5. viewport meta-Tag // Service Worker — Offline-Seite als Fallback: self.addEventListener('fetch', event => { if (event.request.mode === 'navigate') { event.respondWith( fetch(event.request).catch(() => caches.match('/offline.html')) ); } }); // Performance-Optimierungen für PWA Score: // ✅ Precaching: Alle Assets bei SW-Install cachen // ✅ Lazy Loading: Images + nicht-kritisches JS // ✅ Critical CSS inline in <head> // ✅ Fonts: font-display: swap // ✅ WebP/AVIF für Bilder // Lighthouse CI automatisieren: npm install -g @lhci/cli // .lighthouserc.json: { "ci": { "assert": { "assertions": { "categories:pwa": ["error", { "minScore": 0.9 }], "categories:performance": ["warn", { "minScore": 0.8 }] } } } } lhci autorun # In CI/CD Pipeline // Häufige Lighthouse-Fehler + Fixes: // ❌ "Does not provide a valid apple-touch-icon" // → <link rel="apple-touch-icon" href="/icons/icon-180.png"> in <head> // ❌ "Manifest icons are not maskable" // → "purpose": "any maskable" + 10% safe-zone Padding im Icon // ❌ "Service worker does not handle offline" // → start_url muss im Cache sein (precachen!) // ❌ "Page does not work offline" // → offline.html precachen + als Fallback in fetch handler
Claude Code Prompt: "Führe einen Lighthouse-Audit meiner PWA durch und liste alle failing PWA-Checks mit konkreten Fix-Anweisungen auf. Generiere danach die benötigten Code-Änderungen." Claude Code analysiert euer Manifest, Service Worker und HTML systematisch gegen alle Lighthouse-Kriterien.

DevToolsPWA debuggen — Service Worker DevTools

# Nützliche Debugging-Befehle und -Prompts: // Chrome DevTools → Application Tab: // - Service Workers: Status, Update erzwingen, Offline simulieren // - Cache Storage: Gespeicherte Requests anzeigen/löschen // - IndexedDB: Offline-Queue debuggen // - Manifest: Installierbarkeit + Fehler anzeigen // Service Worker im Browser deregistrieren (Dev-Reset): const regs = await navigator.serviceWorker.getRegistrations(); for (const reg of regs) await reg.unregister(); // Update-Check erzwingen: const reg = await navigator.serviceWorker.ready; await reg.update(); // Prompt für Claude Code: "Mein Service Worker cached zu aggressiv. // Implementiere eine Update-Benachrichtigung wenn eine neue SW-Version verfügbar ist" // SW-Update-Notification Pattern: navigator.serviceWorker.addEventListener('controllerchange', () => { showUpdateBanner('Neue Version verfügbar!', () => location.reload()); }); reg.addEventListener('updatefound', () => { reg.installing.addEventListener('statechange', () => { if (reg.installing.state === 'installed' && navigator.serviceWorker.controller) { showUpdateBanner('Update bereit — Seite neu laden?'); } }); });

PWA-Modul im Kurs

Im Claude Code Mastery Kurs: vollständiges PWA-Modul mit Workbox, Push Notifications, Background Sync, IndexedDB und Lighthouse-Optimierung — von Grundlagen bis Production-Deployment.

14 Tage kostenlos testen →