jpskill.com
🛠️ 開発・MCP コミュニティ

web-pwa-offline-first

WebアプリやPWAをオフラインでも快適に使えるように、データ同期の仕組みを使い、ネットワークが不安定な状況でもスムーズに情報更新や操作を可能にするSkill。

📜 元の英語説明(参考)

Local-first architecture with sync queues

🇯🇵 日本人クリエイター向け解説

一言でいうと

WebアプリやPWAをオフラインでも快適に使えるように、データ同期の仕組みを使い、ネットワークが不安定な状況でもスムーズに情報更新や操作を可能にするSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o web-pwa-offline-first.zip https://jpskill.com/download/10299.zip && unzip -o web-pwa-offline-first.zip && rm web-pwa-offline-first.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10299.zip -OutFile "$d\web-pwa-offline-first.zip"; Expand-Archive "$d\web-pwa-offline-first.zip" -DestinationPath $d -Force; ri "$d\web-pwa-offline-first.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して web-pwa-offline-first.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → web-pwa-offline-first フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Offline-First アプリケーションパターン

クイックガイド: ローカルデータを主に使用し、ネットワーク接続を拡張機能として扱うアプリケーションを構築します。IndexedDB (Dexie.js 4.x または idb 8.x 経由) を唯一の信頼できる情報源として使用します。信頼性の高いバックグラウンド同期のために同期キューを実装します。即時のフィードバックのために楽観的な UI パターンを使用します。注: Background Sync API は実験的であり、ブラウザのサポートは限定的です (Chrome/Edge のみ)。


<critical_requirements>

重要: この Skill を使用する前に

すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります (kebab-case、名前付きエクスポート、インポート順序、import type、名前付き定数)

(すべてのオフラインデータに対して、IndexedDB (ラッパーライブラリ経由) を唯一の信頼できる情報源として使用する必要があります)

(同期が必要なすべてのエンティティに、同期メタデータ (_syncStatus、_lastModified、_localVersion) を実装する必要があります)

(オフライン中に変更をキューに入れ、接続が回復したらそれらを処理する必要があります)

(デバイス間での適切な同期を可能にするために、削除にはソフトデリート (墓標) を使用する必要があります)

(すべての同期再試行ロジックに、ジッター付きの指数バックオフを実装する必要があります)

(トランザクション中に IndexedDB 以外の操作を待機しないでください - コントロールがイベントループに戻ると、トランザクションは自動的に閉じられます)

</critical_requirements>


自動検出: offline-first, IndexedDB, Dexie, idb, sync queue, local-first, offline storage, background sync, optimistic UI offline, conflict resolution, CRDT, last-write-wins

使用する場合:

  • ネットワーク接続なしで動作する必要があるアプリケーションを構築する場合
  • 接続が貧弱または断続的なフィールドサービスアプリ
  • 即応性を必要とするメモ取りまたは生産性向上アプリ
  • データの所有権とローカルファーストのアーキテクチャが優先されるアプリ
  • 堅牢なオフラインサポートを必要とするプログレッシブウェブアプリ (PWA)

使用しない場合:

  • 常に最新のサーバーデータを必要とするリアルタイムダッシュボード
  • 即時のサーバー確認を必要とする金融取引
  • キャッシュファーストで十分な単純な読み取り専用アプリ
  • オフライン機能がユーザー価値を追加しないアプリ

ストレージに関する考慮事項:

  • IndexedDB: 利用可能なディスク容量の最大 50% (通常 1GB 以上)、非同期、複雑なクエリをサポート
  • LocalStorage: オリジンあたり 5MB に制限、同期 (UI をブロック)、単純なキーと値のみ
  • Safari: スクリプトで書き込み可能なストレージ (IndexedDB、Cache API) に 7 日間の上限があり、データが削除される可能性があります

詳細なリソース:

  • examples/core.md - 同期可能なエンティティ、リポジトリパターン、同期キュー、ネットワーク検出、楽観的な UI、接続を意識したフェッチ
  • examples/indexeddb.md - Dexie.js のセットアップ、CRUD フック、idb の代替、移行、マルチタブ連携、クォータ管理
  • examples/sync.md - LWW 解決、フィールドレベルのマージ、競合 UI、バージョンベクター、デルタ同期、バックグラウンド同期、プルプッシュ戦略、同期インジケーター
  • reference.md - 意思決定フレームワーク、アンチパターン、トラブルシューティング

<philosophy>

哲学

オフラインファーストは、アプリケーションをローカルデータを主に使用するように構築し、ネットワーク接続を要件ではなく拡張機能として扱う設計哲学です。

コア原則:

  1. ローカルが信頼できる情報源: ローカルデータベースは常に信頼できる情報源です。すべての読み取りと書き込みは、最初にローカルストレージを経由します。サーバー同期はバックグラウンドで行われます。

  2. 即時の応答性: ユーザーはネットワーク操作を待つことはありません。変更はローカルに即座に適用され、後で同期されます。

  3. グレースフルデグラデーション: アプリは完全にオフラインで動作し、オンラインになると拡張され、トランジションをシームレスに処理します。

  4. 同期の透明性: ユーザーは、技術的な専門用語なしに、明確な UI インジケーターを通じてデータの同期状態を理解します。

オフラインファーストのデータフロー:

ユーザーアクション
    |
ローカルデータベース (IndexedDB) <-- 唯一の信頼できる情報源
    |
UI が即座に更新 (楽観的)
    |
同期キュー (バックグラウンド)
    |
サーバー (オンライン時)
    |
競合解決 (必要な場合)
    |
ローカルデータベースが更新

</philosophy>


<patterns>

コアパターン

パターン 1: 同期可能なエンティティ構造

同期が必要なすべてのエンティティには、同期状態を追跡するためのメタデータを含める必要があります。これは基本的なパターンであり、他のすべてのパターンはこれに依存します。

interface SyncableEntity {
  id: string;
  _syncStatus: "synced" | "pending" | "conflicted";
  _lastModified: number;
  _serverTimestamp?: number;
  _localVersion: string;
  _serverVersion?: string;
  _deletedAt?: number; // ソフトデリート墓標
}

これが重要な理由: 同期メタデータがないと、同期が必要なものを追跡したり、競合を検出したり、ソフトデリートを実装したりできません。examples/core.md のパターン 1 で、ファクトリ関数を使用した完全な実装を参照してください。


パターン 2: リポジトリパターン

すべてのデータ操作の単一のアクセスポイントとしてリポジトリを使用し、ローカルストレージと同期キューロジックをカプセル化します。すべての読み取りはローカル DB から行われ、すべての書き込みは最初にローカルに保存され、同期のためにキューに入れられます。

interface DataRepository<T extends SyncableEntity> {
  get(id: string): Promise<T | null>;
  getAll(): Promise<T[]>;
  save(item: T): Promise<void>; // ローカル書き込み + キュー同期
  delete(id: string): Promise<void>; // ソフトデリート + キュー同期
  getPendingCount(): Promise<number>;
}

これが重要な理由: ローカルファーストの書き込みパターン (ローカルに保存し、同期のためにキューに入れる) をカプセル化するため、コンシューマーは両方の操作を管理する必要がありません。examples/core.md のパターン 2 で、完全な実装を参照してください。


パターン 3: 再試行付き同期キュー

オフライン時に操作をキューに入れ、接続が回復したら指数バックオフで確実に処理します。

const MAX_RETRY_ATTEMPTS = 5;
const INITIAL_BACKOFF_MS = 1000;
const MAX_BACKOFF_MS = 30000;

function calculateBackoff(attempt: number): number {
  const exponentialDelay = Math.min(
    INITIAL_BACKOFF_MS * Math.pow(2, attempt),
    MAX_BACKOFF_MS,
  );
  const jitter = exponentialDelay * 0.5 * (Math.ra
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Offline-First Application Patterns

Quick Guide: Build applications that work primarily with local data, treating network connectivity as an enhancement. Use IndexedDB (via Dexie.js 4.x or idb 8.x) as the single source of truth. Implement sync queues for reliable background synchronization. Use optimistic UI patterns for instant feedback. Note: Background Sync API is experimental with limited browser support (Chrome/Edge only).


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST use IndexedDB (via wrapper library) as the single source of truth for all offline data)

(You MUST implement sync metadata (_syncStatus, _lastModified, _localVersion) on ALL entities that need synchronization)

(You MUST queue mutations during offline and process them when connectivity returns)

(You MUST use soft deletes (tombstones) for deletions to enable proper sync across devices)

(You MUST implement exponential backoff with jitter for ALL sync retry logic)

(You MUST NOT await non-IndexedDB operations mid-transaction - transactions auto-close when control returns to event loop)

</critical_requirements>


Auto-detection: offline-first, IndexedDB, Dexie, idb, sync queue, local-first, offline storage, background sync, optimistic UI offline, conflict resolution, CRDT, last-write-wins

When to use:

  • Building applications that must work without network connectivity
  • Field service apps with poor or intermittent connectivity
  • Note-taking or productivity apps requiring instant responsiveness
  • Apps where data ownership and local-first architecture is prioritized
  • Progressive Web Apps (PWAs) needing robust offline support

When NOT to use:

  • Real-time dashboards requiring always-fresh server data
  • Financial transactions requiring immediate server confirmation
  • Simple read-only apps where cache-first is sufficient
  • Apps where offline capability adds no user value

Storage Considerations:

  • IndexedDB: Up to 50% of available disk space (typically 1GB+), async, supports complex queries
  • LocalStorage: Limited to 5MB per origin, synchronous (blocks UI), simple key-value only
  • Safari: 7-day cap on script-writable storage (IndexedDB, Cache API) may evict data

Detailed Resources:

  • examples/core.md - Syncable entities, repository pattern, sync queue, network detection, optimistic UI, connection-aware fetching
  • examples/indexeddb.md - Dexie.js setup, CRUD hooks, idb alternative, migrations, multi-tab coordination, quota management
  • examples/sync.md - LWW resolution, field-level merge, conflict UI, version vectors, delta sync, background sync, pull-push strategy, sync indicators
  • reference.md - Decision frameworks, anti-patterns, troubleshooting

<philosophy>

Philosophy

Offline-first is a design philosophy where applications are built to work primarily with local data, treating network connectivity as an enhancement rather than a requirement.

Core Principles:

  1. Local is the Source of Truth: The local database is always authoritative. All reads and writes go through local storage first. Server sync happens in the background.

  2. Immediate Responsiveness: Users never wait for network operations. Changes are applied locally instantly, synced later.

  3. Graceful Degradation: Apps work fully offline, enhance when online, and handle transitions seamlessly.

  4. Sync Transparency: Users understand their data's sync state through clear UI indicators without technical jargon.

The Offline-First Data Flow:

User Action
    |
Local Database (IndexedDB) <-- Single Source of Truth
    |
UI Updates Immediately (Optimistic)
    |
Sync Queue (Background)
    |
Server (When Online)
    |
Conflict Resolution (If Needed)
    |
Local Database Updated

</philosophy>


<patterns>

Core Patterns

Pattern 1: Syncable Entity Structure

Every entity that needs synchronization must include metadata for tracking sync state. This is the foundational pattern - all other patterns depend on it.

interface SyncableEntity {
  id: string;
  _syncStatus: "synced" | "pending" | "conflicted";
  _lastModified: number;
  _serverTimestamp?: number;
  _localVersion: string;
  _serverVersion?: string;
  _deletedAt?: number; // Soft delete tombstone
}

Why this matters: Without sync metadata, you cannot track what needs syncing, detect conflicts, or implement soft deletes. See examples/core.md Pattern 1 for full implementation with factory functions.


Pattern 2: Repository Pattern

Use a repository as the single access point for all data operations, encapsulating local storage and sync queue logic. All reads come from local DB, all writes save locally first then queue for sync.

interface DataRepository<T extends SyncableEntity> {
  get(id: string): Promise<T | null>;
  getAll(): Promise<T[]>;
  save(item: T): Promise<void>; // Local write + queue sync
  delete(id: string): Promise<void>; // Soft delete + queue sync
  getPendingCount(): Promise<number>;
}

Why this matters: Encapsulates the local-first write pattern (save locally, queue for sync) so consumers don't need to manage both operations. See examples/core.md Pattern 2 for full implementation.


Pattern 3: Sync Queue with Retry

Queue operations when offline, process reliably with exponential backoff when connectivity returns.

const MAX_RETRY_ATTEMPTS = 5;
const INITIAL_BACKOFF_MS = 1000;
const MAX_BACKOFF_MS = 30000;

function calculateBackoff(attempt: number): number {
  const exponentialDelay = Math.min(
    INITIAL_BACKOFF_MS * Math.pow(2, attempt),
    MAX_BACKOFF_MS,
  );
  const jitter = exponentialDelay * 0.5 * (Math.random() * 2 - 1);
  return Math.floor(exponentialDelay + jitter);
}

Why this matters: Without retry logic, transient network failures cause permanent data loss. Jitter prevents thundering herd when many clients reconnect simultaneously. See examples/core.md Pattern 3 for full queue implementation.


Pattern 4: Network Status Detection

Don't rely solely on navigator.onLine (returns true behind captive portals, dead WiFi). Verify with actual health check requests.

async function checkConnectivity(): Promise<boolean> {
  if (!navigator.onLine) return false;
  try {
    const response = await fetch("/api/health", {
      method: "HEAD",
      cache: "no-store",
    });
    return response.ok;
  } catch {
    return false;
  }
}

Why this matters: navigator.onLine only checks for a network interface, not actual internet connectivity. See examples/core.md Pattern 4 for full status manager with slow connection detection.


Pattern 5: Optimistic UI with Rollback

Update UI immediately, store previous value for rollback if sync fails. Return a rollback function from each optimistic update.

See examples/core.md Pattern 5 for full implementation with rollback support.


Pattern 6: Connection-Aware Data Fetching

Fetch from network when online, fall back to cache when offline. Return source metadata ("network" | "cache") so UI can indicate data freshness.

See examples/core.md Pattern 6 for full implementation with timeout and cache fallback.


Pattern 7: Conflict Resolution Strategies

Three strategies ordered by complexity:

  1. Last-Write-Wins (LWW): Simplest. Most recent timestamp wins. Good for independent values. See examples/sync.md Pattern 18.

  2. Field-Level Merge: Only conflicts where both sides changed the same field. Preserves non-conflicting changes from both sides. See examples/sync.md Pattern 19.

  3. Version Vectors: Detect true concurrent modifications without clock synchronization. Use when timestamp-based approaches fail due to clock drift. See examples/sync.md Pattern 21.

For collaborative text editing, use a CRDT library (separate concern from this skill).

</patterns>


<red_flags>

RED FLAGS

High Priority:

  • Never use hard deletes - soft delete with tombstones or data will "resurrect" after sync
  • Never trust navigator.onLine alone - verify with actual network request
  • Never block UI on network operations - save locally first, sync in background
  • Never await fetch() or setTimeout() inside an IndexedDB transaction - transaction auto-closes

Medium Priority:

  • No retry logic on sync operations - transient failures cause permanent data loss
  • No sync status indicators in UI - users lose trust when they can't see sync state
  • Unbounded sync queue - can exhaust storage or cause OOM during batch processing
  • Timestamp-only conflict detection - clock drift makes this unreliable for concurrent edits

Gotchas & Edge Cases:

  • Safari (iOS 13.4+) enforces a 7-day cap on script-writable storage if the user doesn't interact with the site; request navigator.storage.persist() and encourage home screen install
  • navigator.storage.estimate() requires HTTPS; returns { usage: 0, quota: 0 } in unsecured contexts
  • Background Sync API is Chrome/Edge only (experimental) - always implement online event listener as fallback
  • IndexedDB compound index queries use arrays: .where("[userId+completed]").equals([userId, 1]) (1 = true)
  • Dexie useLiveQuery returns undefined while loading, not null - check with === undefined
  • Multiple tabs can cause write conflicts - use BroadcastChannel for coordination (see examples/indexeddb.md Pattern 16)

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md

(You MUST use IndexedDB (via wrapper library) as the single source of truth for all offline data)

(You MUST implement sync metadata (_syncStatus, _lastModified, _localVersion) on ALL entities that need synchronization)

(You MUST queue mutations during offline and process them when connectivity returns)

(You MUST use soft deletes (tombstones) for deletions to enable proper sync across devices)

(You MUST implement exponential backoff with jitter for ALL sync retry logic)

(You MUST NOT await non-IndexedDB operations mid-transaction - transactions auto-close when control returns to event loop)

Failure to follow these rules will result in data loss, sync conflicts, and poor offline user experience.

</critical_reminders>