Agent skill

Pwa Development

by alinaqi in alinaqi/maggy

Progressive Web Apps - service workers, caching strategies, offline, Workbox

MITAuto-check passedBackend & APIs

Install Pwa Development

skills CLI
$ npx skills add alinaqi/maggy --skill pwa-development -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install alinaqi/maggy pwa-development --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/alinaqi/maggy.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/pwa-development .claude/skills/pwa-development && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
pwa-development
GitHub stars
707
Token cost
~6.1k tokens
SKILL.md length
527 words
Files
1
Skills in repo
71
Repo updated
First seen
Licence
MIT

At a glance

Progressive Web Apps - service workers, caching strategies, offline, Workbox

  • Tasks that involve Caching
  • SKILL.md covers Core PWA Requirements, Web App Manifest, Service Worker Patterns and Caching Strategies, plus 4 more sections
  • Calls npm and npx; needs VAPID_PUBLIC_KEY

What it does

Pwa Development is an agent skill from alinaqi/maggy. Progressive Web Apps - service workers, caching strategies, offline, Workbox

Its SKILL.md is about 6.1k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Backend & APIs, covering Caching. The repository describes itself as: What started as an opinionated Claude Code setup kit is now an autonomous AI engineering command center. The licence is MIT.

When your agent uses it

  • Tasks that involve Caching

Example prompts

  • “/pwa-development”

Requirements

  • Node.js

What it can do on your machine

Read from SKILL.md and the folder at commit 72a456e. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • npm
    • npx

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npm and npx, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names these keys or tokens, usually read from environment variables:

    • VAPID_PUBLIC_KEY

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Pwa Development loads about 6.1k tokens when it runs. Until then it costs about 23 tokens; SKILL.md has 527 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~23
When it runs · the whole SKILL.md, loaded when a task matches
~6.1k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from alinaqi/maggy at commit 72a456e, republished under its MIT licence (© alinaqi). 527 words, ~6,084 tokens.

Download SKILL.mdSave it as .claude/skills/pwa-development/SKILL.md (or your agent's skills folder).
name
pwa-development
description
Progressive Web Apps - service workers, caching strategies, offline, Workbox
when-to-use
When building PWA features - service workers, caching, offline support
user-invocable
false
paths
**/sw.*, **/service-worker.*, **/workbox-config.*, **/manifest.json
effort
medium

PWA Development Skill

Purpose: Build Progressive Web Apps that work offline, install like native apps, and deliver fast, reliable experiences across all devices.


Core PWA Requirements

┌─────────────────────────────────────────────────────────────────┐
│  THE THREE PILLARS OF PWA                                       │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  1. HTTPS                                                       │
│     Required for service workers and security.                  │
│     localhost allowed for development.                          │
│                                                                 │
│  2. SERVICE WORKER                                              │
│     JavaScript that runs in background.                         │
│     Enables offline, caching, push notifications.               │
│                                                                 │
│  3. WEB APP MANIFEST                                            │
│     JSON file describing app metadata.                          │
│     Enables installation and app-like experience.               │
├─────────────────────────────────────────────────────────────────┤
│  INSTALLABILITY CRITERIA (Chrome)                               │
│  ─────────────────────────────────────────────────────────────  │
│  • HTTPS (or localhost)                                         │
│  • Service worker with fetch handler                            │
│  • Web app manifest with: name, icons (192px + 512px),          │
│    start_url, display: standalone/fullscreen/minimal-ui         │
└─────────────────────────────────────────────────────────────────┘

Web App Manifest

Required Fields
json
{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "description": "A description of what the app does",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512-maskable.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}
Enhanced Manifest (Full Features)
json
{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "description": "A full-featured PWA",
  "start_url": "/?source=pwa",
  "scope": "/",
  "display": "standalone",
  "orientation": "portrait-primary",
  "background_color": "#ffffff",
  "theme_color": "#3367D6",
  "dir": "ltr",
  "lang": "en",
  "categories": ["productivity", "utilities"],

  "icons": [
    { "src": "/icons/icon-72.png", "sizes": "72x72", "type": "image/png" },
    { "src": "/icons/icon-96.png", "sizes": "96x96", "type": "image/png" },
    { "src": "/icons/icon-128.png", "sizes": "128x128", "type": "image/png" },
    { "src": "/icons/icon-144.png", "sizes": "144x144", "type": "image/png" },
    { "src": "/icons/icon-152.png", "sizes": "152x152", "type": "image/png" },
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-384.png", "sizes": "384x384", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icons/icon-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "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": "New Item",
      "short_name": "New",
      "description": "Create a new item",
      "url": "/new?source=shortcut",
      "icons": [{ "src": "/icons/shortcut-new.png", "sizes": "192x192" }]
    }
  ],

  "share_target": {
    "action": "/share",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "title",
      "text": "text",
      "url": "url",
      "files": [{ "name": "files", "accept": ["image/*"] }]
    }
  },

  "protocol_handlers": [
    {
      "protocol": "web+myapp",
      "url": "/handle?url=%s"
    }
  ],

  "file_handlers": [
    {
      "action": "/open-file",
      "accept": {
        "text/plain": [".txt"]
      }
    }
  ]
}
Manifest Checklist
  • name and short_name defined
  • start_url set (use query param for analytics)
  • display set to standalone or fullscreen
  • Icons: 192x192 and 512x512 minimum
  • Maskable icon included for Android adaptive icons
  • theme_color matches app design
  • background_color for splash screen
  • Screenshots for richer install UI (optional)
  • Shortcuts for quick actions (optional)

Service Worker Patterns

Basic Service Worker
javascript
// sw.js
const CACHE_NAME = 'app-cache-v1';
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles/main.css',
  '/scripts/app.js',
  '/offline.html'
];

// Install: Cache static assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(STATIC_ASSETS))
      .then(() => self.skipWaiting())
  );
});

// Activate: Clean old caches
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())
  );
});

// Fetch: Serve from cache, fall back to network
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cached) => cached || fetch(event.request))
      .catch(() => caches.match('/offline.html'))
  );
});
Registration
javascript
// main.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const registration = await navigator.serviceWorker.register('/sw.js', {
        scope: '/'
      });
      console.log('SW registered:', registration.scope);
    } catch (error) {
      console.error('SW registration failed:', error);
    }
  });
}

Caching Strategies

Strategy Selection Guide
StrategyUse CaseDescription
Cache FirstStatic assets (CSS, JS, images)Check cache, fall back to network
Network FirstAPI responses, dynamic contentTry network, fall back to cache
Stale While RevalidateSemi-static content (avatars, articles)Serve cache immediately, update in background
Network OnlyNon-cacheable requests (analytics)Always use network
Cache OnlyOffline-only assetsOnly serve from cache
Cache First (Offline First)
javascript
// Best for: Static assets that rarely change
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image' ||
      event.request.destination === 'style' ||
      event.request.destination === 'script') {
    event.respondWith(
      caches.match(event.request)
        .then((cached) => {
          if (cached) return cached;
          return fetch(event.request).then((response) => {
            const clone = response.clone();
            caches.open(CACHE_NAME).then((cache) => {
              cache.put(event.request, clone);
            });
            return response;
          });
        })
    );
  }
});
Network First (Fresh First)
javascript
// Best for: API data, frequently updated content
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/api/')) {
    event.respondWith(
      fetch(event.request)
        .then((response) => {
          const clone = response.clone();
          caches.open(CACHE_NAME).then((cache) => {
            cache.put(event.request, clone);
          });
          return response;
        })
        .catch(() => caches.match(event.request))
    );
  }
});
Stale While Revalidate
javascript
// Best for: Content that's okay to be slightly outdated
self.addEventListener('fetch', (event) => {
  if (event.request.url.includes('/articles/')) {
    event.respondWith(
      caches.open(CACHE_NAME).then((cache) => {
        return cache.match(event.request).then((cached) => {
          const fetchPromise = fetch(event.request).then((response) => {
            cache.put(event.request, response.clone());
            return response;
          });
          return cached || fetchPromise;
        });
      })
    );
  }
});

Why Workbox?
  • Battle-tested caching strategies
  • Precaching with revision management
  • Background sync for offline forms
  • Automatic cache cleanup
  • TypeScript support
Installation
bash
npm install workbox-webpack-plugin  # Webpack
npm install @vite-pwa/vite-plugin   # Vite
Workbox with Vite
javascript
// vite.config.js
import { VitePWA } from 'vite-plugin-pwa';

export default {
  plugins: [
    VitePWA({
      registerType: 'autoUpdate',
      includeAssets: ['favicon.ico', 'robots.txt', 'apple-touch-icon.png'],
      manifest: {
        name: 'My App',
        short_name: 'App',
        theme_color: '#ffffff',
        icons: [
          { src: 'pwa-192x192.png', sizes: '192x192', type: 'image/png' },
          { src: 'pwa-512x512.png', sizes: '512x512', type: 'image/png' }
        ]
      },
      workbox: {
        globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
        runtimeCaching: [
          {
            urlPattern: /^https:\/\/api\.example\.com\/.*/i,
            handler: 'NetworkFirst',
            options: {
              cacheName: 'api-cache',
              expiration: {
                maxEntries: 100,
                maxAgeSeconds: 60 * 60 * 24 // 24 hours
              }
            }
          },
          {
            urlPattern: /\.(?:png|jpg|jpeg|svg|gif)$/,
            handler: 'CacheFirst',
            options: {
              cacheName: 'image-cache',
              expiration: {
                maxEntries: 50,
                maxAgeSeconds: 60 * 60 * 24 * 30 // 30 days
              }
            }
          }
        ]
      }
    })
  ]
};
Workbox Manual Service Worker
javascript
// sw.js
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';

// Precache static assets (generated by build tool)
precacheAndRoute(self.__WB_MANIFEST);

// Cache images
registerRoute(
  ({ request }) => request.destination === 'image',
  new CacheFirst({
    cacheName: 'images',
    plugins: [
      new CacheableResponsePlugin({ statuses: [0, 200] }),
      new ExpirationPlugin({
        maxEntries: 60,
        maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days
      })
    ]
  })
);

// Cache API responses
registerRoute(
  ({ url }) => url.pathname.startsWith('/api/'),
  new NetworkFirst({
    cacheName: 'api-responses',
    plugins: [
      new CacheableResponsePlugin({ statuses: [0, 200] }),
      new ExpirationPlugin({
        maxEntries: 100,
        maxAgeSeconds: 24 * 60 * 60 // 24 hours
      })
    ]
  })
);

// Cache page navigations
registerRoute(
  ({ request }) => request.mode === 'navigate',
  new NetworkFirst({
    cacheName: 'pages',
    plugins: [
      new CacheableResponsePlugin({ statuses: [0, 200] })
    ]
  })
);

Offline Experience

Offline Page
html
<!-- offline.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Offline - App Name</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      display: flex;
      align-items: center;
      justify-content: center;
      min-height: 100vh;
      margin: 0;
      background: #f5f5f5;
    }
    .offline-content {
      text-align: center;
      padding: 2rem;
    }
    .offline-icon { font-size: 4rem; }
    h1 { color: #333; }
    p { color: #666; }
    button {
      background: #3367D6;
      color: white;
      border: none;
      padding: 0.75rem 1.5rem;
      border-radius: 4px;
      cursor: pointer;
      font-size: 1rem;
    }
  </style>
</head>
<body>
  <div class="offline-content">
    <div class="offline-icon">📡</div>
    <h1>You're offline</h1>
    <p>Check your connection and try again.</p>
    <button onclick="location.reload()">Retry</button>
  </div>
</body>
</html>
Offline Detection
javascript
// Online/offline status handling
function updateOnlineStatus() {
  const status = navigator.onLine ? 'online' : 'offline';
  document.body.dataset.connectionStatus = status;

  if (!navigator.onLine) {
    showNotification('You are offline. Some features may be unavailable.');
  }
}

window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
updateOnlineStatus();
Background Sync (Queue Offline Actions)
javascript
// sw.js with Workbox
import { BackgroundSyncPlugin } from 'workbox-background-sync';
import { registerRoute } from 'workbox-routing';
import { NetworkOnly } from 'workbox-strategies';

const bgSyncPlugin = new BackgroundSyncPlugin('formQueue', {
  maxRetentionTime: 24 * 60 // Retry for 24 hours
});

registerRoute(
  ({ url }) => url.pathname === '/api/submit',
  new NetworkOnly({
    plugins: [bgSyncPlugin]
  }),
  'POST'
);
javascript
// main.js - Queue form submission
async function submitForm(data) {
  try {
    const response = await fetch('/api/submit', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });
    return response.json();
  } catch (error) {
    // Will be retried by background sync when online
    showNotification('Saved offline. Will sync when connected.');
  }
}

App-Like Features

Install Prompt
javascript
let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  showInstallButton();
});

async function installApp() {
  if (!deferredPrompt) return;

  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;

  console.log(`User ${outcome === 'accepted' ? 'accepted' : 'dismissed'} install`);
  deferredPrompt = null;
  hideInstallButton();
}

window.addEventListener('appinstalled', () => {
  console.log('App installed');
  deferredPrompt = null;
});
Detecting Standalone Mode
javascript
// Check if running as installed PWA
function isInstalledPWA() {
  return window.matchMedia('(display-mode: standalone)').matches ||
         window.navigator.standalone === true; // iOS
}

// Listen for display mode changes
window.matchMedia('(display-mode: standalone)')
  .addEventListener('change', (e) => {
    console.log('Display mode:', e.matches ? 'standalone' : 'browser');
  });
Push Notifications
javascript
// Request permission
async function requestNotificationPermission() {
  const permission = await Notification.requestPermission();
  if (permission === 'granted') {
    await subscribeToPush();
  }
  return permission;
}

// Subscribe to push
async function subscribeToPush() {
  const registration = await navigator.serviceWorker.ready;
  const subscription = await registration.pushManager.subscribe({
    userVisibleOnly: true,
    applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
  });

  // Send subscription to server
  await fetch('/api/push/subscribe', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(subscription)
  });
}

// sw.js - Handle push events
self.addEventListener('push', (event) => {
  const data = event.data.json();
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png',
      badge: '/icons/badge-72.png',
      data: { url: data.url }
    })
  );
});

// Handle notification click
self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  event.waitUntil(
    clients.openWindow(event.notification.data.url)
  );
});
Share Target
javascript
// sw.js - Handle share target
self.addEventListener('fetch', (event) => {
  if (event.request.url.endsWith('/share') &&
      event.request.method === 'POST') {
    event.respondWith((async () => {
      const formData = await event.request.formData();
      const title = formData.get('title');
      const text = formData.get('text');
      const url = formData.get('url');

      // Store or process shared content
      // Redirect to app with shared data
      return Response.redirect(`/?shared=true&title=${encodeURIComponent(title)}`);
    })());
  }
});

Performance Optimization

Critical Rendering Path
html
<!-- Inline critical CSS -->
<style>
  /* Critical above-the-fold styles */
</style>

<!-- Preload important resources -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/scripts/app.js" as="script">

<!-- Defer non-critical CSS -->
<link rel="stylesheet" href="/styles/main.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/styles/main.css"></noscript>
Image Optimization
html
<!-- Responsive images -->
<img
  src="/images/hero-800.webp"
  srcset="
    /images/hero-400.webp 400w,
    /images/hero-800.webp 800w,
    /images/hero-1200.webp 1200w
  "
  sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
  alt="Hero image"
  loading="lazy"
  decoding="async"
>

<!-- Modern formats with fallback -->
<picture>
  <source srcset="/images/hero.avif" type="image/avif">
  <source srcset="/images/hero.webp" type="image/webp">
  <img src="/images/hero.jpg" alt="Hero image" loading="lazy">
</picture>
Code Splitting
javascript
// Dynamic imports for route-based splitting
const routes = {
  '/': () => import('./pages/Home.js'),
  '/about': () => import('./pages/About.js'),
  '/settings': () => import('./pages/Settings.js')
};

async function loadPage(path) {
  const loader = routes[path];
  if (loader) {
    const module = await loader();
    return module.default;
  }
}

Testing PWA

Lighthouse Audit
bash
# Run Lighthouse from CLI
npx lighthouse https://your-app.com --view

# Key metrics to check:
# - PWA badge (installable, offline-ready)
# - Performance score
# - Best practices
# - Accessibility
Manual Testing Checklist
  • Installability

    • Install prompt appears on desktop Chrome
    • Can be added to home screen on mobile
    • App opens in standalone mode after install
  • Offline Support

    • App loads when offline (airplane mode)
    • Cached pages display correctly
    • Offline fallback page shows for uncached routes
    • Background sync works when coming back online
  • Performance

    • First Contentful Paint < 1.8s
    • Largest Contentful Paint < 2.5s
    • Time to Interactive < 3.8s
    • Cumulative Layout Shift < 0.1
  • Service Worker

    • SW registers successfully
    • Static assets cached on install
    • SW updates correctly (new version)
    • No stale cache issues
  • Manifest

    • All required fields present
    • Icons display correctly
    • Theme color applied
    • Splash screen shows on launch
Show full SKILL.md (194 more words)Show less
Testing Service Worker Updates
javascript
// Force update check
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.ready.then((registration) => {
    registration.update();
  });
}

// Listen for updates
navigator.serviceWorker.addEventListener('controllerchange', () => {
  // New service worker activated
  window.location.reload();
});

Project Structure

project/
├── public/
│   ├── manifest.json           # Web app manifest
│   ├── sw.js                   # Service worker (if not bundled)
│   ├── offline.html            # Offline fallback page
│   ├── robots.txt
│   └── icons/
│       ├── icon-72.png
│       ├── icon-96.png
│       ├── icon-128.png
│       ├── icon-144.png
│       ├── icon-152.png
│       ├── icon-192.png
│       ├── icon-384.png
│       ├── icon-512.png
│       ├── icon-maskable.png   # For adaptive icons
│       ├── apple-touch-icon.png
│       └── favicon.ico
├── src/
│   ├── sw.js                   # Service worker source (if bundled)
│   ├── pwa/
│   │   ├── install.js          # Install prompt handling
│   │   ├── offline.js          # Offline detection
│   │   └── push.js             # Push notification handling
│   └── ...
└── tests/
    └── pwa/
        ├── manifest.test.js
        ├── sw.test.js
        └── offline.test.js

Common Mistakes

MistakeFix
Missing maskable iconAdd icon with "purpose": "maskable"
No offline fallbackCreate offline.html and cache it
Cache never expiresUse ExpirationPlugin with Workbox
SW caches too aggressivelyUse appropriate strategies per resource type
No update mechanismImplement skipWaiting() + reload prompt
Broken install promptEnsure manifest meets all criteria
No HTTPS in productionConfigure SSL certificate
Large cache sizeSet maxEntries and maxAgeSeconds
Stale API responsesUse NetworkFirst for dynamic data
Missing start_url trackingAdd query param: /?source=pwa

PWA Development Checklist

Before Launch
  • HTTPS configured (production)
  • Manifest complete with all required fields
  • Icons in all required sizes (192, 512, maskable)
  • Service worker registered and working
  • Offline page created and cached
  • Cache strategies defined for all resource types
  • Install prompt handling implemented
  • Lighthouse PWA audit passes
After Launch
  • Monitor cache sizes
  • Test SW updates don't break app
  • Track PWA installs via analytics
  • Test on multiple devices/browsers
  • Monitor Core Web Vitals
  • Set up push notification flow (if needed)

Framework-Specific Guides

Next.js
bash
npm install next-pwa
javascript
// next.config.js
const withPWA = require('next-pwa')({
  dest: 'public',
  disable: process.env.NODE_ENV === 'development'
});

module.exports = withPWA({
  // Your Next.js config
});
Create React App
bash
# CRA 4+ has PWA support built-in
npx create-react-app my-pwa --template cra-template-pwa
Vite (Any Framework)
bash
npm install vite-plugin-pwa -D

See Workbox with Vite section above for configuration.


Quick Reference

Caching Strategy Cheat Sheet
Static Assets (CSS, JS, images)     → Cache First
API Responses                        → Network First
User-generated content              → Stale While Revalidate
Analytics, non-cacheable            → Network Only
Offline-only assets                 → Cache Only
Manifest Minimum Requirements
json
{
  "name": "App Name",
  "short_name": "App",
  "start_url": "/",
  "display": "standalone",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
  ]
}
Service Worker Lifecycle
1. Register → 2. Install → 3. Activate → 4. Fetch
     ↓              ↓            ↓           ↓
  Load app    Cache assets  Clean old   Serve requests
                            caches      from cache/network

© alinaqi, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/pwa-development of alinaqi/maggy.

Open the folder on GitHubat commit 72a456e

Compare with similar skills

Pwa Development next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Pwa Development compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Pwa Development this skillalinaqi/maggy707—~6.1kAutomated safety check: PassMIT
Stripe Projectsfossasia/eventyay1.7k5 repos~2kAutomated safety check: NotesApache-2.0
FoundatioFoundatioFx/Foundatio2.1k—~3.9kAutomated safety check: PassApache-2.0
Wp Block Themesgambitph/Stackable3513 repos~985Automated safety check: PassGPL-3.0
Wp Performancegambitph/Stackable3513 repos~1.5kAutomated safety check: PassGPL-3.0
Effect Portable Patternsmillionco/expect3.6k—~3.7kAutomated safety check: PassCustom licence

Similar skills

  • Stripe Projects

    fossasia/eventyay

    A skill your agent uses when the user wants to provision infrastructure or third-party services using Stripe Projects.

    1.7k GitHub starsUsed in 5 repos~2k tokens
    Backend & APIsAuto-check: notes
  • Foundatio

    FoundatioFx/Foundatio

    A skill your agent uses when working with Foundatio infrastructure abstractions for .NET -- caching, queuing, messaging, file storage, distributed locking, or background jobs.

    2.1k GitHub stars~3.9k tokensUpdated yesterday
    Backend & APIsAuto-check passed
  • Wp Block Themes

    gambitph/Stackable

    A skill your agent uses when developing WordPress block themes: theme.json (global settings/styles), templates and template parts, patterns, style variations, and Site Editor troubleshooting (style…

    351 GitHub starsUsed in 3 repos~985 tokens
    Backend & APIsAuto-check passed
  • Wp Performance

    gambitph/Stackable

    A skill your agent uses when investigating or improving WordPress performance (backend-only agent): profiling and measurement (WP-CLI profile/doctor, Server-Timing, Query Monitor via REST headers)…

    351 GitHub starsUsed in 3 repos~1.5k tokens
    Backend & APIsAuto-check passed
  • Effect Portable Patterns

    millionco/expect

    Portable Effect patterns for robust promise execution. An agent skill from millionco/expect.

    3.6k GitHub stars~3.7k tokensUpdated 5 mo ago
    Backend & APIsAuto-check passed
  • FastAPI-Redis SDK Development

    redis/fastapi-redis-sdk

    Official

    Guides development on the fastapi-redis-sdk library itself - its connection lifecycle, dependency-injected caching, and async/sync bridging.

    404 GitHub stars~2.5k tokensUpdated 8 days ago
    Backend & APIsAuto-check: notes

More from alinaqi/maggy

All 71 skills in this repo
  • Aeo Optimization

    alinaqi/maggy

    AI Engine Optimization - semantic triples, page templates, content clusters for AI citations

    707 GitHub stars~3.7k tokensUpdated 15 days ago
    Auto-check passed
  • Agent Teams

    alinaqi/maggy

    Claude Code Agent Teams - default team-based development with strict TDD pipeline enforcement

    707 GitHub stars~5k tokensUpdated 15 days ago
    Auto-check: notes
  • AI Models

    alinaqi/maggy

    Latest AI models reference - Claude, OpenAI, Gemini, Eleven Labs, Replicate

    707 GitHub stars~4.1k tokensUpdated 15 days ago
    Auto-check passed
  • Android Java

    alinaqi/maggy

    Android Java development with MVVM, ViewBinding, and Espresso testing

    707 GitHub stars~3.9k tokensUpdated 15 days ago
    Auto-check: notes
  • Android Kotlin

    alinaqi/maggy

    Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testing

    707 GitHub stars~3k tokensUpdated 15 days ago
    Auto-check passed
  • Autonomous Testing

    alinaqi/maggy

    AI-driven testing agent that auto-discovers, generates, executes, evaluates, and fixes tests for any project type

    707 GitHub stars~1.1k tokensUpdated 15 days ago
    Auto-check passed

Categories

Questions about Pwa Development

What does Pwa Development do?

Progressive Web Apps - service workers, caching strategies, offline, Workbox. Pwa Development is an agent skill from alinaqi/maggy.

When should I use Pwa Development?

Pwa Development fits situations like: tasks that involve Caching.

How do I install Pwa Development in Claude Code?

Run `npx skills add alinaqi/maggy --skill pwa-development -a claude-code`. Or copy the skill folder (skills/pwa-development in alinaqi/maggy) into .claude/skills/pwa-development in your project. Claude Code loads it when a task matches its description.

How do I install Pwa Development in Codex?

Run `npx skills add alinaqi/maggy --skill pwa-development -a codex`. Or copy the skill folder (skills/pwa-development in alinaqi/maggy) into .agents/skills/pwa-development in your project. Codex loads it when a task matches its description.

Can I use Pwa Development in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add alinaqi/maggy --skill pwa-development -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/pwa-development, .gemini/skills/pwa-development, .github/skills/pwa-development and .opencode/skills/pwa-development in your project.

What does Pwa Development need to run?

Going by SKILL.md and its folder, Pwa Development needs the command-line tools its instructions call (npm and npx) and credentials named VAPID_PUBLIC_KEY. Our summary lists: Node.js.

Does Pwa Development access the network?

SKILL.md contains no URLs. Its commands use npm and npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Pwa Development safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Pwa Development use?

Pwa Development is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Pwa Development use?

About 6.1k tokens (SKILL.md is roughly 24k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Pwa Development?

Skills that share tags, products or a category with Pwa Development: Stripe Projects (fossasia/eventyay, 1.7k stars), Foundatio (FoundatioFx/Foundatio, 2.1k stars), Wp Block Themes (gambitph/Stackable, 351 stars) and Wp Performance (gambitph/Stackable, 351 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Pwa Development?

alinaqi (a GitHub user) maintains it in alinaqi/maggy, which has 707 GitHub stars. The repository holds 71 skills in this directory. The repository was last updated on September 24, 2026.

Source: alinaqi/maggy on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.