mindrally

pwa-development

3
0
# Install this skill:
npx skills add Mindrally/skills --skill "pwa-development"

Install specific skill from multi-skill repository

# Description

Progressive Web App development guidelines covering service workers, caching strategies, offline functionality, and installability

# SKILL.md


name: pwa-development
description: Progressive Web App development guidelines covering service workers, caching strategies, offline functionality, and installability


Progressive Web App Development Guidelines

You are an expert in building Progressive Web Applications with offline-first capabilities.

Core Principles

  • Design for offline-first experience
  • Implement proper caching strategies
  • Ensure fast loading and smooth performance
  • Follow web app manifest best practices
  • Provide native-like experience

Web App Manifest

{
  "name": "My Progressive Web App",
  "short_name": "MyPWA",
  "description": "A description of your app",
  "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"
    }
  ]
}

Service Worker Registration

// Register service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const registration = await navigator.serviceWorker.register('/sw.js');
      console.log('SW registered:', registration.scope);
    } catch (error) {
      console.error('SW registration failed:', error);
    }
  });
}

Service Worker Implementation

// sw.js
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
  '/',
  '/index.html',
  '/styles.css',
  '/app.js',
  '/offline.html'
];

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

// Activate event - cleanup old caches
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    })
  );
  self.clients.claim();
});

// Fetch event - serve from cache or network
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    }).catch(() => {
      return caches.match('/offline.html');
    })
  );
});

Caching Strategies

Cache First (Static Assets)

async function cacheFirst(request) {
  const cached = await caches.match(request);
  return cached || fetch(request);
}

Network First (Dynamic Content)

async function networkFirst(request) {
  try {
    const response = await fetch(request);
    const cache = await caches.open(CACHE_NAME);
    cache.put(request, response.clone());
    return response;
  } catch {
    return caches.match(request);
  }
}

Stale While Revalidate

async function staleWhileRevalidate(request) {
  const cache = await caches.open(CACHE_NAME);
  const cached = await cache.match(request);

  const fetchPromise = fetch(request).then((response) => {
    cache.put(request, response.clone());
    return response;
  });

  return cached || fetchPromise;
}

Background Sync

// Register sync in main app
async function registerSync() {
  const registration = await navigator.serviceWorker.ready;
  await registration.sync.register('sync-data');
}

// Handle sync in service worker
self.addEventListener('sync', (event) => {
  if (event.tag === 'sync-data') {
    event.waitUntil(syncData());
  }
});

async function syncData() {
  const data = await getQueuedData();
  await fetch('/api/sync', {
    method: 'POST',
    body: JSON.stringify(data)
  });
}

Push Notifications

// Request permission
async function requestNotificationPermission() {
  const permission = await Notification.requestPermission();
  if (permission === 'granted') {
    const registration = await navigator.serviceWorker.ready;
    const subscription = await registration.pushManager.subscribe({
      userVisibleOnly: true,
      applicationServerKey: PUBLIC_VAPID_KEY
    });
    // Send subscription to server
  }
}

// Handle push in service worker
self.addEventListener('push', (event) => {
  const data = event.data.json();
  event.waitUntil(
    self.registration.showNotification(data.title, {
      body: data.body,
      icon: '/icons/icon-192.png'
    })
  );
});

Offline Detection

// Check online status
window.addEventListener('online', () => {
  console.log('Back online');
  syncPendingData();
});

window.addEventListener('offline', () => {
  console.log('Offline mode');
  showOfflineIndicator();
});

Performance Optimization

  • Implement app shell architecture
  • Use lazy loading for routes and components
  • Optimize images with responsive formats
  • Minimize JavaScript bundle size
  • Use code splitting

Testing

  • Test offline functionality
  • Verify caching behavior
  • Test on various network conditions
  • Validate manifest and icons
  • Use Lighthouse for PWA audits

Best Practices

  • Serve over HTTPS
  • Provide meaningful offline experience
  • Handle service worker updates gracefully
  • Implement proper error handling
  • Add loading states and skeletons

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.