Skip to content

PWA Features

The template is a Progressive Web App with offline support, installability, and native-like behavior.

Installation

Manual Install

On Mobile (Chrome):

  1. Open your deployed PWA in Chrome
  2. Tap the browser menu (⋮)
  3. Select "Add to Home Screen"

On Desktop:

  1. Open the PWA in Chrome
  2. Click the install icon in the address bar

Programmatic Install

Use the included usePWAInstall hook:

tsx
import { usePWAInstall } from '@/hooks/usePWAInstall';

function InstallBanner() {
  const { isInstallable, isInstalled, promptInstall } = usePWAInstall();

  // Already installed as PWA
  if (isInstalled) return null;

  // Can't be installed (not supported or already prompted)
  if (!isInstallable) return null;

  return (
    <button onClick={promptInstall}>
      Install App
    </button>
  );
}

The hook provides:

  • isInstallable - Browser supports install and prompt is available
  • isInstalled - Running as installed PWA (standalone mode)
  • promptInstall() - Trigger the install prompt

Service Worker & Caching

The template uses next-pwa with pre-configured caching strategies:

ContentStrategyCache Duration
Solana APINetworkFirst5 minutes
ImagesCacheFirst30 days
FontsCacheFirst1 year
JS/CSSStaleWhileRevalidate7 days

Configuration

Edit next.config.ts to customize caching:

ts
const withPWA = withPWAInit({
  dest: 'public',
  disable: process.env.NODE_ENV === 'development',
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/api\..*\.solana\.com/,
      handler: 'NetworkFirst',
      options: {
        cacheName: 'solana-api-cache',
        expiration: {
          maxEntries: 50,
          maxAgeSeconds: 60 * 5,
        },
      },
    },
    // Add more patterns...
  ],
});

Offline Support

Checking Online Status

tsx
import { useState, useEffect } from 'react';

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);

  useEffect(() => {
    setIsOnline(navigator.onLine);

    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

Offline UI

tsx
function OfflineBanner() {
  const isOnline = useOnlineStatus();

  if (isOnline) return null;

  return (
    <div className="fixed top-0 inset-x-0 bg-yellow-500 text-black text-center py-2">
      You're offline. Some features may be unavailable.
    </div>
  );
}

TIP

Cached pages and assets remain available offline. Wallet transactions require network connectivity.

Notifications

Request permission and send notifications:

tsx
async function enableNotifications() {
  if (!('Notification' in window)) {
    console.log('Notifications not supported');
    return false;
  }

  const permission = await Notification.requestPermission();
  return permission === 'granted';
}

function sendNotification(title: string, body: string) {
  if (Notification.permission !== 'granted') return;

  new Notification(title, {
    body,
    icon: '/icons/icon-192x192.png',
  });
}

WARNING

Request notification permission only after user interaction (e.g., button click). Prompting on page load leads to high denial rates.

Standalone Detection

Check if running as installed PWA:

tsx
function useIsStandalone() {
  const [isStandalone, setIsStandalone] = useState(false);

  useEffect(() => {
    const standalone =
      window.matchMedia('(display-mode: standalone)').matches ||
      (navigator as any).standalone === true;

    setIsStandalone(standalone);
  }, []);

  return isStandalone;
}

Or use the isInstalled property from usePWAInstall.

Testing

Service Worker

  1. Build for production: npm run build
  2. Start: npm run start
  3. Open DevTools → Application → Service Workers
  4. Verify service worker is registered

Offline Mode

  1. DevTools → Network → Check "Offline"
  2. Verify cached pages load
  3. Verify offline UI appears

Installation

  1. Deploy to HTTPS host
  2. Open in Chrome
  3. Verify install prompt appears
  4. Test installed app behavior

Released under the MIT License.