Build progressive web applications with Google Antigravity IDE
# PWA Development Guide for Google Antigravity
Create powerful Progressive Web Applications that deliver native-like experiences using Google Antigravity IDE.
## Service Worker Setup
```typescript
// public/sw.ts
import { precacheAndRoute, cleanupOutdatedCaches } from "workbox-precaching";
import { registerRoute, NavigationRoute, Route } from "workbox-routing";
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from "workbox-strategies";
import { ExpirationPlugin } from "workbox-expiration";
import { CacheableResponsePlugin } from "workbox-cacheable-response";
declare const self: ServiceWorkerGlobalScope;
// Precache static assets
precacheAndRoute(self.__WB_MANIFEST);
cleanupOutdatedCaches();
// Cache API responses with network-first strategy
registerRoute(
({ url }) => url.pathname.startsWith("/api/"),
new NetworkFirst({
cacheName: "api-cache",
plugins: [
new CacheableResponsePlugin({ statuses: [0, 200] }),
new ExpirationPlugin({ maxEntries: 100, maxAgeSeconds: 60 * 60 })
]
})
);
// Cache images with cache-first strategy
registerRoute(
({ request }) => request.destination === "image",
new CacheFirst({
cacheName: "image-cache",
plugins: [
new CacheableResponsePlugin({ statuses: [0, 200] }),
new ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 30 * 24 * 60 * 60 })
]
})
);
// Cache Google Fonts
registerRoute(
({ url }) => url.origin === "https://fonts.googleapis.com",
new StaleWhileRevalidate({ cacheName: "google-fonts-stylesheets" })
);
// Offline fallback for navigation
const navigationRoute = new NavigationRoute(
new NetworkFirst({
cacheName: "pages-cache",
plugins: [new CacheableResponsePlugin({ statuses: [0, 200] })]
}),
{ denylist: [/^\/api\//] }
);
registerRoute(navigationRoute);
// Background sync for offline actions
import { BackgroundSyncPlugin } from "workbox-background-sync";
import { Queue } from "workbox-background-sync";
const bgSyncQueue = new Queue("offline-queue", {
onSync: async ({ queue }) => {
let entry;
while ((entry = await queue.shiftRequest())) {
try {
await fetch(entry.request);
} catch (error) {
await queue.unshiftRequest(entry);
throw error;
}
}
}
});
self.addEventListener("fetch", (event) => {
if (event.request.method === "POST" && event.request.url.includes("/api/sync")) {
const bgSyncLogic = async () => {
try {
return await fetch(event.request.clone());
} catch (error) {
await bgSyncQueue.pushRequest({ request: event.request });
return new Response(JSON.stringify({ queued: true }), {
headers: { "Content-Type": "application/json" }
});
}
};
event.respondWith(bgSyncLogic());
}
});
```
## Web App Manifest
```json
{
"name": "My PWA Application",
"short_name": "MyPWA",
"description": "A progressive web application built with Google Antigravity",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#000000",
"orientation": "portrait-primary",
"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": "1280x720", "type": "image/png", "form_factor": "wide" },
{ "src": "/screenshots/mobile.png", "sizes": "750x1334", "type": "image/png", "form_factor": "narrow" }
],
"shortcuts": [
{ "name": "Create New", "short_name": "New", "url": "/new", "icons": [{ "src": "/icons/new.png", "sizes": "96x96" }] }
],
"share_target": {
"action": "/share",
"method": "POST",
"enctype": "multipart/form-data",
"params": { "title": "title", "text": "text", "url": "url", "files": [{ "name": "media", "accept": ["image/*", "video/*"] }] }
}
}
```
## Install Prompt Handling
```typescript
// hooks/usePWAInstall.ts
import { useState, useEffect, useCallback } from "react";
interface BeforeInstallPromptEvent extends Event {
prompt(): Promise<void>;
userChoice: Promise<{ outcome: "accepted" | "dismissed" }>;
}
export function usePWAInstall() {
const [installPrompt, setInstallPrompt] = useState<BeforeInstallPromptEvent | null>(null);
const [isInstalled, setIsInstalled] = useState(false);
useEffect(() => {
const handleBeforeInstall = (e: Event) => {
e.preventDefault();
setInstallPrompt(e as BeforeInstallPromptEvent);
};
const handleAppInstalled = () => {
setIsInstalled(true);
setInstallPrompt(null);
};
window.addEventListener("beforeinstallprompt", handleBeforeInstall);
window.addEventListener("appinstalled", handleAppInstalled);
// Check if already installed
if (window.matchMedia("(display-mode: standalone)").matches) {
setIsInstalled(true);
}
return () => {
window.removeEventListener("beforeinstallprompt", handleBeforeInstall);
window.removeEventListener("appinstalled", handleAppInstalled);
};
}, []);
const promptInstall = useCallback(async () => {
if (!installPrompt) return false;
await installPrompt.prompt();
const { outcome } = await installPrompt.userChoice;
if (outcome === "accepted") {
setIsInstalled(true);
}
setInstallPrompt(null);
return outcome === "accepted";
}, [installPrompt]);
return { canInstall: !!installPrompt && !isInstalled, isInstalled, promptInstall };
}
```
## Push Notifications
```typescript
// lib/push-notifications.ts
export async function subscribeToPush(): Promise<PushSubscription | null> {
if (!"serviceWorker" in navigator || !"PushManager" in window) {
return null;
}
const registration = await navigator.serviceWorker.ready;
const permission = await Notification.requestPermission();
if (permission !== "granted") return null;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_KEY!)
});
await fetch("/api/push/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(subscription)
});
return subscription;
}
function urlBase64ToUint8Array(base64String: string): Uint8Array {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
const rawData = window.atob(base64);
return Uint8Array.from([...rawData].map((char) => char.charCodeAt(0)));
}
```
## Best Practices
1. **Cache strategically** based on content type and update frequency
2. **Implement offline fallbacks** for critical functionality
3. **Use background sync** for offline form submissions
4. **Optimize app shell** for instant loading
5. **Handle install prompts** at the right moment
6. **Test across devices** and network conditions
7. **Monitor service worker** updates carefully
Google Antigravity provides intelligent PWA scaffolding and caching strategy recommendations for optimal offline experiences.This pwa prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their pwa implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For pwa projects, consider mentioning your framework version, coding style, and any specific libraries you're using.