pwa-expert について
インストール可能でオフライン対応のプログレッシブウェブアプリを構築する開発スキルです。マニフェスト、Service Workerのキャッシュ、インストールプロンプト、バックグラウンド同期、更新フローに加え、Next.jsの選択肢も扱います。
主な機能
- 01Web App Manifestの設定
- 02Service Workerの登録とキャッシュ
- 03インストールプロンプトの処理
- 04オフライン時の代替表示と状態管理
- 05バックグラウンド同期と更新フロー
- 06Next.js向けPWAの選択肢
活用シーン
- 01Webアプリをインストール可能にする
- 02オフライン利用向けにアセットをキャッシュ
- 03接続回復まで操作を保留
- 04アプリの更新をユーザーに通知
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ curiositech/ some_claude_skills --skill pwa-expertインストール後、Claude Code で/pwa-expertを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
.claude-plugin/plugin.jsonreferences/nextjs-integration.mdservice-worker-patterns.mdbackground-sync.mdinstall-prompt.mdupdate-flow.mdoffline-handling.md詳細
プログレッシブウェブアプリのエキスパート
Service Worker、スマートキャッシュ、ネイティブアプリのような操作感で、インストール可能なオフライン対応ウェブアプリを構築します。
このスキルを使う場面
- モバイル/デスクトップでウェブアプリをインストール可能にする
- オフライン機能を実装する
- Service Worker のキャッシュ戦略を設定する
- インストールプロンプト(
beforeinstallprompt)を処理する - オフラインファーストのアプリにバックグラウンド同期を実装する
- PWA の更新フローを管理する
- ウェブアプリマニフェストを作成する
このスキルを使わない場面
- ネイティブアプリの開発 → React Native、Flutter、またはネイティブ SDK を使用してください
- 一般的なウェブパフォーマンス → Lighthouse/パフォーマンス監査ツールを使用してください
- サーバーサイドレンダリングの問題 → Next.js/フレームワーク固有のドキュメントを参照してください
- プッシュ通知のみ → 専用のプッシュ通知サービスを検討してください
- シンプルな静的サイト → PWA のオーバーヘッドに見合わない場合があります
基本概念
PWA をインストール可能にする要件
- HTTPS(または開発時の localhost)
- 必須フィールドを含む ウェブアプリマニフェスト
- fetch ハンドラーを備えた Service Worker
- アイコン(最低 192×192 と 512×512)
PWA の構成
┌─────────────────────────────────────────┐
│ Your App (React/Next.js) │
├─────────────────────────────────────────┤
│ Service Worker (sw.js) │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Cache │ │ Network Fetch │ │
│ │ Storage │ │ Handling │ │
│ └─────────────┘ └─────────────────┘ │
├─────────────────────────────────────────┤
│ manifest.json │
│ (App identity, icons, display mode) │
└─────────────────────────────────────────┘
ウェブアプリマニフェスト
完全な manifest.json
{
"name": "Junkie Buds 4 Life",
"short_name": "JB4L",
"description": "Recovery support app",
"start_url": "/",
"scope": "/",
"display": "standalone",
"orientation": "portrait-primary",
"background_color": "#1a1410",
"theme_color": "#1a1410",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/icons/icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"shortcuts": [
{
"name": "Find Meetings",
"short_name": "Meetings",
"url": "/meetings?source=shortcut",
"icons": [{ "src": "/icons/meetings-96.png", "sizes": "96x96" }]
}
]
}
表示モード
| モード | 説明 |
|---|---|
fullscreen | ブラウザー UI を表示せず、全画面で表示します |
standalone | URL バーのないアプリのような表示です(推奨) |
minimal-ui | 一部のブラウザーコントロールを表示します |
browser | 通常のブラウザータブです |
HTML でのリンク
<head>
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#1a1410" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
</head>
Service Worker の基本
登録
// lib/pwa.ts
export async function registerServiceWorker() {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/',
});
return registration;
} catch (error) {
console.error('SW registration failed:', error);
}
}
}
// Call on app mount
useEffect(() => {
registerServiceWorker();
}, []);
Service Worker の基本構造
// public/sw.js
const CACHE_NAME = 'myapp-v1';
const STATIC_ASSETS = ['/', '/offline', '/manifest.json'];
// Install: Cache static assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
// Activate: Clean old caches
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
)
);
self.clients.claim();
});
// Fetch: Handle requests (see references for strategies)
self.addEventListener('fetch', (event) => {
event.respondWith(handleFetch(event.request));
});
参照: キャッシュ戦略の実装については
references/service-worker-patterns.mdを参照してください
キャッシュ戦略
| 戦略 | 最適な用途 | トレードオフ |
|---|---|---|
| Cache-First | 静的アセット、フォント、画像 | キャッシュが更新されるまで古い状態になります |
| Network-First | API データ、ユーザーコンテンツ | 遅くなり、接続が必要です |
| Stale-While-Revalidate | 最新性と速度のバランス | バックグラウンドで更新します |
| Network-Only | 認証、リアルタイムデータ | オフラインでは利用できません |
| Cache-Only | バージョン管理されたアセット | 更新されません |
参照: 完全な実装については
references/service-worker-patterns.mdを参照してください
インストールプロンプト
beforeinstallprompt イベントを処理して、カスタムのインストール UI を表示します。
// Basic pattern
const [deferredPrompt, setDeferredPrompt] = useState(null);
useEffect(() => {
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
setDeferredPrompt(e);
});
}, []);
const handleInstall = async () => {
if (deferredPrompt) {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
// outcome: 'accepted' or 'dismissed'
}
};
参照:
usePWAInstallフックとコンポーネントの完全な実装についてはreferences/install-prompt.mdを参照してください
オフラインでの操作体験
主なパターン:
- ナビゲーションに失敗した場合のオフラインページへのフォールバック
- 接続状態を検出する
useOnlineStatusフック - ユーザーに知らせるオフラインバナー
参照: 実装については
references/offline-handling.mdを参照してください
バックグラウンド同期
オフライン中にアクションをキューに登録し、接続が回復したら実行します。
// In Service Worker
self.addEventListener('sync', (event) => {
if (event.tag === 'sync-data') {
event.waitUntil(syncPendingData());
}
});
// In App - trigger sync
const registration = await navigator.serviceWorker.ready;
await registration.sync.register('sync-data');
参照: IndexedDB との完全な統合については
references/background-sync.mdを参照してください
更新フロー
新しいバージョンが利用可能になったことをユーザーに通知します。
// Basic pattern
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker?.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New version available - show update prompt
}
});
});
参照:
usePWAUpdateフックと更新戦略についてはreferences/update-flow.mdを参照してください
Next.js との統合
Next.js で PWA を実現する方法:
- next-pwa - 標準の Next.js サーバーで動作します
- カスタム SW -
output: 'export'(静的サイト)で必要です - Workbox CLI - ビルド後に SW を生成します
参照: 詳細な設定については
references/nextjs-integration.mdを参照してください
クイックリファレンス
| タスク | 解決方法 |
|---|---|
| インストール済みか確認する | window.matchMedia('(display-mode: standalone)').matches |
| SW の更新を強制する | registration.update() |
| すべてのキャッシュを消去する | caches.keys().then(keys => keys.forEach(k => caches.delete(k))) |
| オンラインか確認する | navigator.onLine |
| SW の登録を取得する | navigator.serviceWorker.ready |
| 待機をスキップする | SW 内で self.skipWaiting() を実行します |
| 制御を取得する | SW 内で self.clients.claim() を実行します |
PWA のテスト
Chrome DevTools
- Application タブ → Manifest、Service Workers、Cache Storage
- Lighthouse → PWA 監査
- Network → Offline チェックボックスでシミュレーション
デバッグチェックリスト
- マニフェストが読み込まれる(Application → Manifest)
- SW が登録される(Application → Service Workers)
- キャッシュにデータが格納される(Application → Cache Storage)
- インストールプロンプトが表示される(beforeinstallprompt を Console で確認)
- オフラインページが動作する(Network → Offline)
- 更新フローが動作する(更新を実行し、プロンプトを確認)
参考資料
詳細な実装については /references/ を参照してください。
service-worker-patterns.md- キャッシュ戦略の実装install-prompt.md-usePWAInstallフックとインストールコンポーネントoffline-handling.md- オフラインページ、ステータスフック、バナーbackground-sync.md- IndexedDB を使ったバックグラウンド同期update-flow.md- 更新の検出とユーザープロンプトnextjs-integration.md- Next.js の PWA 設定オプション