mcpField
N
nextjs-pwa

nextjs-pwa

github.com/just-mpm開発65 回インストール
リポジトリ

nextjs-pwa について

Next.js でプログレッシブウェブアプリを実装する開発者向けガイドです。インストール対応、オフライン利用、キャッシュについて、標準機能と Serwist の設定、制約、移行時の要点を説明します。

主な機能

  1. 01標準機能と Serwist の比較
  2. 02ウェブアプリマニフェストの設定
  3. 03Service Worker の生成と登録
  4. 04キャッシュとオフライン戦略の利用
  5. 05Serwist のバージョン変更を確認

活用シーン

  1. 01Next.js アプリにインストール対応を追加
  2. 02オフラインキャッシュとフォールバックページを追加
  3. 03Pages Router プロジェクトの実装方法を選ぶ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/just-mpm/conformai --skill nextjs-pwa

インストール後、Claude Code で/nextjs-pwaを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
implementation-approaches.md
native-nextjs-implementation.md
serwist-implementation.md

詳細

Next.js PWA 実装ガイド(2025)

2025年時点で最も新しい手法を用いて、Next.js でプログレッシブウェブアプリ(PWA)を実装するための包括的なガイドです。

クイックスタートの判断ツリー

この判断ツリーに従って、適切な実装方法を選択してください。

Need PWA with Next.js?
│
├─ Basic installability only?
│  └─ Use: Native Next.js PWA Support
│     ✓ Zero dependencies
│     ✓ Built-in App Router support
│     ✓ Simple manifest.ts/json
│     ✗ No offline caching
│
└─ Need offline functionality?
   └─ Use: Serwist Package
      ✓ Advanced caching strategies
      ✓ Service worker management
      ✓ Background sync
      ✓ Push notifications
      ⚠ Requires configuration

アプローチ 1:Next.js のネイティブ PWA サポート

概要

Next.js の組み込み機能を使った、依存関係ゼロの PWA 実装です。

使用するケース
  • ✅ App Router(/app ディレクトリ)を使用している
  • ✅ 基本的な PWA 機能(インストール可能性、マニフェスト)が必要
  • ✅ 複雑なオフラインキャッシュは必要ない
  • ✅ 外部依存関係をゼロにしたい
  • ✅ Next.js の公式推奨に従いたい
主な機能
  • 2024年秋から組み込みでサポート(公式 PWA ガイドが公開)
  • 外部パッケージは不要
  • manifest.ts、manifest.json、または manifest.webmanifest によるマニフェスト生成
  • App Router とシームレスに連携
  • MetadataRoute.Manifest 型による TypeScript サポート
制限事項
  • オフライン機能は自動ではサポートされない
  • サービスワーカーは生成されない
  • 高度なキャッシュ戦略はない
  • オフライン機能にはサービスワーカーの手動実装が必要
クイック実装

1. マニフェストを作成(app/manifest.ts):

import type { MetadataRoute } from 'next'

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: 'Your App Name',
    short_name: 'App',
    description: 'Your app description',
    start_url: '/',
    display: 'standalone',
    background_color: '#ffffff',
    theme_color: '#000000',
    icons: [
      {
        src: '/icon-192.png',
        sizes: '192x192',
        type: 'image/png',
      },
      {
        src: '/icon-512.png',
        sizes: '512x512',
        type: 'image/png',
      },
    ],
  }
}

2. メタタグを追加(app/layout.tsx):

export const metadata = {
  manifest: '/manifest.webmanifest',
  appleWebApp: {
    capable: true,
    statusBarStyle: 'default',
    title: 'Your App Name',
  },
}

3. HTTPS でデプロイ(本番環境の PWA では必須)


アプローチ 2:Serwist パッケージ

概要

オフラインサポート、キャッシュ戦略、サービスワーカー管理を備えた高度な PWA 実装です。

使用するケース
  • ✅ オフライン機能が必要
  • ✅ 高度なキャッシュ戦略が必要
  • ✅ バックグラウンド同期が必要
  • ✅ プッシュ通知が必要
  • ✅ 複雑なサービスワーカーのロジックが必要
  • ✅ きめ細かなキャッシュ制御が必要
バージョン情報(2025年11月更新)
  • Serwist 最新安定版: 9.2.1+
  • プレビュー版: 10.0.0-preview(開発中)
  • 破壊的変更: v9.0.0 で API に大幅な変更が導入(2024年3月)
  • 必要な Node.js: 18.0.0+(22.x を推奨)
  • TypeScript: 5.0.0+
主な機能
  • サービスワーカーの自動生成
  • 静的アセットの事前キャッシュ
  • 実行時キャッシュ戦略(CacheFirst、NetworkFirst、StaleWhileRevalidate)
  • バックグラウンド同期
  • プッシュ通知のサポート
  • オフライン時のフォールバックページ
  • TypeScript サポート
Turbopack との互換性(2025年11月更新)

重要なお知らせ:

✅ 本番ビルド(next build):完全に互換性あり - Turbopack で通常どおり動作します ⚠️ 開発(next dev --turbo):警告が表示されますが、完全に機能します

開発時の警告への対処方法:

# Option 1: Suppress warning (Recommended for dev)
# Add to .env.local
SERWIST_SUPPRESS_TURBOPACK_WARNING="1"

# Option 2: Use webpack in development
npm run dev -- --webpack

注: 本番ビルドでは --webpack フラグは不要です。Serwist は本番環境で Turbopack とネイティブに連携します。

インストール
npm i @serwist/next && npm i -D serwist
# or
yarn add @serwist/next && yarn add -D serwist
# or
pnpm add @serwist/next && pnpm add -D serwist
クイック実装

1. next.config.js を設定:

import withSerwistInit from '@serwist/next'

const withSerwist = withSerwistInit({
  swSrc: 'app/sw.ts',
  swDest: 'public/sw.js',
  cacheOnNavigation: true,
  reloadOnOnline: true,
  disable: process.env.NODE_ENV === 'development', // Optional
})

export default withSerwist({
  // Your Next.js config
})

2. サービスワーカーを作成(app/sw.ts):

import { Serwist } from 'serwist'

const serwist = new Serwist({
  precacheEntries: self.__SW_MANIFEST,
  skipWaiting: true,
  clientsClaim: true,
  navigationPreload: true,
  runtimeCaching: [
    {
      urlPattern: /^https:\/\/fonts\.(?:googleapis|gstatic)\.com\/.*/i,
      handler: 'CacheFirst',
      options: {
        cacheName: 'google-fonts',
        expiration: {
          maxEntries: 4,
          maxAgeSeconds: 365 * 24 * 60 * 60, // 1 year
        },
      },
    },
    {
      urlPattern: /\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'static-font-assets',
        expiration: {
          maxEntries: 4,
          maxAgeSeconds: 7 * 24 * 60 * 60, // 1 week
        },
      },
    },
    {
      urlPattern: /\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'static-image-assets',
        expiration: {
          maxEntries: 64,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
      },
    },
    {
      urlPattern: /\/_next\/image\?url=.+$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'next-image',
        expiration: {
          maxEntries: 64,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
      },
    },
    {
      urlPattern: /\.(?:js)$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'static-js-assets',
        expiration: {
          maxEntries: 32,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
      },
    },
    {
      urlPattern: /\.(?:css|less)$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'static-style-assets',
        expiration: {
          maxEntries: 32,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
      },
    },
    {
      urlPattern: /\/_next\/data\/.+\/.+\.json$/i,
      handler: 'StaleWhileRevalidate',
      options: {
        cacheName: 'next-data',
        expiration: {
          maxEntries: 32,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
      },
    },
    {
      urlPattern: /\/api\/.*/i,
      handler: 'NetworkFirst',
      method: 'GET',
      options: {
        cacheName: 'apis',
        expiration: {
          maxEntries: 16,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
        networkTimeoutSeconds: 10,
      },
    },
    {
      urlPattern: /.*/i,
      handler: 'NetworkFirst',
      options: {
        cacheName: 'others',
        expiration: {
          maxEntries: 32,
          maxAgeSeconds: 24 * 60 * 60, // 1 day
        },
        networkTimeoutSeconds: 10,
      },
    },
  ],
})

serwist.addEventListeners()

3. サービスワーカーを登録(app/layout.tsx):

'use client'

import { useEffect } from 'react'

export default function RootLayout({ children }) {
  useEffect(() => {
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker
        .register('/sw.js')
        .then((registration) => {
          console.log('Service Worker registered:', registration)
        })
        .catch((error) => {
          console.error('Service Worker registration failed:', error)
        })
    }
  }, [])

  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

4. tsconfig.json を更新:

{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext", "webworker"],
    "types": ["@serwist/next/typings"]
  }
}

5. マニフェストを作成(ネイティブアプローチと同じ)


破壊的変更:Serwist v9.0.0+

古いバージョンから移行する場合は、次の重要な変更に注意してください。

1. インポートの変更
// ❌ OLD (v8.x and earlier)
import { installSerwist } from '@serwist/sw'

// ✅ NEW (v9.0.0+)
import { Serwist } from 'serwist'
2. 初期化方法の変更
// ❌ OLD
const sw = installSerwist({ /* config */ })

// ✅ NEW
const serwist = new Serwist({ /* config */ })
serwist.addEventListeners() // Required!
3. パッケージの統合
# ❌ OLD
npm i @serwist/precaching @serwist/routing @serwist/strategies

# ✅ NEW (all in one)
npm i -D serwist

比較:ネイティブと Serwist

機能Next.js ネイティブSerwist
セットアップの複雑さ⭐ シンプル⭐⭐⭐ 中程度
依存関係✅ ゼロ⚠️ 2 パッケージ
オフラインサポート❌ 手動✅ 自動
キャッシュ戦略❌ なし✅ 複数
サービスワーカー❌ 手動✅ 自動生成
App Router サポート✅ ネイティブ✅ 完全
Pages Router サポート⚠️ 限定的✅ 完全
TypeScript✅ 組み込み✅ 完全
本番環境対応✅ はい✅ はい
Turbopack との互換性✅ はい✅ はい(環境変数が必要)

よくある問題と解決策

問題 1:サービスワーカーが更新されない

解決策: サービスワーカーにバージョンを設定するか、skipWaiting: true を使用してください

問題 2:キャッシュされたコンテンツが更新されない

解決策: 適切なキャッシュ無効化戦略を実装するか、maxAgeSeconds を小さくしてください

問題 3:開発環境での Turbopack 警告

解決策: .env.local に SERWIST_SUPPRESS_TURBOPACK_WARNING="1" を追加してください

問題 4:HTTPS が必要というエラー

解決策: HTTPS ドメインにデプロイしてください(テストには localhost を使用できます)

問題 5:アイコンが表示されない

解決策: アイコンが public/ ディレクトリにあり、マニフェストのパスが正しいことを確認してください


推奨事項

新規プロジェクトの場合:
  1. Next.js 標準の PWA から始める(シンプルで、依存関係がありません)
  2. オフライン機能が必要になった場合にのみ Serwist にアップグレードする
既存のプロジェクトの場合:
  1. next-pwa から移行しますか? → Serwist を使用してください(直接置き換えられます)
  2. すでにカスタムサービスワーカーを使用していますか? → そのまま使用するか、Serwist にリファクタリングしてください
  3. Pages Router を使用していますか? → Serwist を推奨します(より優れたサポート)
本番環境の場合:
  1. ✅ 常に HTTPS を使用してください
  2. ✅ オフライン機能を十分にテストしてください
  3. ✅ 適切なエラー処理を実装してください
  4. ✅ サービスワーカーの更新を監視してください
  5. ✅ アセットごとに適切なキャッシュ戦略を使用してください

リソース


バージョン履歴

  • 2025年11月: Serwist のバージョン情報を更新し、Turbopack との互換性に関する説明を明確化
  • 2024年秋: Next.js 標準の PWA サポートが公式にドキュメント化
  • 2024年3月: Serwist v9.0.0 を破壊的変更とともにリリース
  • 2023年: Workbox の最新の代替として Serwist を作成

完全かつ最新の情報については、以下のファイルを参照してください:

C:\Users\tetu_.claude\skills\nextjs-pwa\references\serwist-implementation.md C:\Users\tetu_.claude\skills\nextjs-pwa\references\native-nextjs-implementation.md C:\Users\tetu_.claude\skills\nextjs-pwa\references\implementation-approaches.md

最終更新日: 2025年11月13日

コメント