next-cache-components-adoption について
Next.js の App Router プロジェクトで Cache Components を導入し、ビルド成功を目指すスキルです。開発サーバーとブラウザーで動作を確かめながら、ルートごとの変更を案内します。
主な機能
- 01App Router とプロジェクト要件の事前確認
- 02段階的または直接導入する移行経路
- 03ルート単位の移行ワークフロー
- 04next dev とブラウザーによる実行時確認
- 05ルート変更後のビルド検証
活用シーン
- 01App Router アプリでの Cache Components 有効化
- 02プリレンダリングを妨げるルートの移行
- 03リクエスト時データと同期 I/O の問題診断
MCP 対応
実行時検証には next-dev-loop の利用を推奨しており、Next.js の `/_next/mcp` エンドポイントと agent-browser を使います。このスキル自体が MCP ツールを提供するとは記載されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vercel/ next.js --skill next-cache-components-adoptionインストール後、Claude Code で/next-cache-components-adoptionを入力して読み込まれたか確認します。
同梱ファイルreferences/dev-only-validations.mdreferences/per-page-decisions.md
詳細
next-cache-components-adoption
アプリで Cache Components を有効にし、ビルドが成功するまで順を追って進めます。このスキルでは作業の順序を示します。エラーごとの対処方法は、開発オーバーレイの修正カードとビルドのターミナル出力に記載されています。Cache Components への移行ガイドは、このスキルで適用する概念と API ごとの対処方法に関する正式なリファレンスです。このスキルの手順でパターン("use cache"、cacheLife、<Suspense> の配置など)が参照され、詳しい説明が必要な場合は参照してください。
必要条件
-
App Router プロジェクトであること。 Cache Components は App Router の機能です。
cacheComponents: trueはpages/のルートには何もしません。プロジェクトにpages/またはsrc/pages/のツリーがあり、app/またはsrc/app/のツリーがない場合は、そこで止めてユーザーに伝えてください。Pages から App への移行は独立したプロジェクトであり、このスキルの対象外です。ハイブリッドアプリ(pages/とapp/の両方がある状態)は問題ありません。このフラグの影響を受けるのはapp/のルートのみで、pages/のルートには影響せず、オプトアウトも必要ありません。 -
アプリディレクトリが特定できていること。 まず
next.config.{js,ts,mjs,cjs}を探してください。ここがプロジェクトのルートです。そうしないと、サブディレクトリから呼び出されたエージェントが誤ったcwdを基準にapp/を探し、何も見つけられない場合があります。その配下にあるapp/とsrc/app/を探し、以降のコマンドと glob は、存在するいずれかのディレクトリを基準に実行してください。両方ある場合、Next.js はapp/をビルドし、src/app/は参照しないため、そのルートは隠れた状態になり、ビルドされません。そのことをユーザーに伝え、どちらのツリーを移行するか確認してください。どちらかを独断で選んではいけません。 -
アプリを実行できること。 一連の作業では
next devとブラウザーを使って検証するため、アプリが起動する必要があります。データベースや必須の環境変数をインポート時に読み込む場合(たとえば、DATABASE_URLがないとエラーを投げるenv.tsなど)は、手順 1 の前に、実際の環境または用意したローカルデータでアプリが実際に起動することを確認してください。実行できないアプリでは移行を検証できません。 -
Next.js 16.3 以降であること。 このリリースで、このスキルが必要とする機能(トップレベルの
cacheComponents、export const instant、開発オーバーレイでのインスタントナビゲーション検証に関する警告、cache-components-instant-falsecodemod)が導入されています。next --versionの結果が 16.3 未満の場合は、先にアップグレードしてください。- バージョンごとの codemod を適用するには、
npx @next/codemod@latest upgrade latestを実行します。 - codemod でカバーされない内容については、該当するバージョンアップグレードガイド(たとえばバージョン 16)を確認してください。
- バージョンごとの codemod を適用するには、
-
互換性のない設定キーがないこと。
cacheComponents: trueを有効にすると、dynamic、revalidate、またはfetchCacheをエクスポートしているファイルが残っている場合にエラーになります。codemod を実行する前に、これらのエクスポートをすべて確認し、移行ガイドのキー別セクションに従ってください。各値の変換方法については、ガイドを正としてください。cache-components-instant-falsecodemod は、これらの設定を削除しません。 -
experimental.dynamicIOは致命的なエラーになります。 これはトップレベルのcacheComponentsに名前が変更されており、古いキーが残っているとビルドを実行する前に処理が中断されます。まず削除するか、cacheComponents: trueに置き換えてください。experimental.useCacheは非推奨のエイリアスとして引き続き受け付けられますが、cacheComponents: trueを設定すれば不要になるため、明確にする目的で削除してください。
notes
-
フラグを有効にする前にビルドが成功する状態にはしません。 アプリがすでに
"use cache"を使用している場合、フラグを有効にする前のビルドはplease enable the feature flag cacheComponentsというエラーになります。最初に行うのはフラグの有効化です(Incremental では codemod の前に、Direct ではルートの修正前に有効化します)。ビルドを成功させてから有効にするものではありません。退行のように見えないよう、開始時の概要にこの点を記載してください。 -
既存のキャッシュはそのまま使えます。 移行ガイドの
fetchとunstable_cacheのセクションに従ってください。Cache Components を有効にするためだけに、それらを書き換えないでください。 -
オフラインのドキュメント。 ガイドへのリンクには、
node_modules/next/dist/docs/以下にオフライン版があります(Next.js 16.2 から同梱)。ディレクトリ構成は順序が分かるように番号が付いています(例:node_modules/next/dist/docs/01-app/02-guides/migrating-to-cache-components.md)。番号付きのプレフィックスが分からない場合は、find node_modules/next/dist/docs -name '<slug>.md'で見つけられます。/docs/messages/*のエラーページは同梱されていません。 -
ドキュメントが同梱されていない古いバージョン。 作業を始める前に、ユーザーに
npx @next/codemod@latest agents-mdの実行を提案してください。バージョンに合ったコピーを.next-docs/にダウンロードし、インデックスをAGENTS.mdに書き込みます。ユーザーのリポジトリ内のファイルに変更を加えるため、まず確認し、希望がある場合にのみ実行してください。
作業の進め方
ループはひとつです。ルートツリーを上から順にたどり、機能をひとつずつ next dev とブラウザーで確認しながら、各ルートに適用していきます。ビルドは機能ごとの最終確認であり、作業の中心ではありません。
ステップ 1 では、まずすべてのルートを検証対象から外すか、進めながらルートを修正するかを選びます。どちらを選んでもループは同じです。
-
事前作業を抑える場合(Incremental)。 codemod を実行し、codemod で対応できない点を修正してから、以前は静的レンダリングが必要だったルートを完全に移行します。それ以外のルートでは検証対象外の設定を維持し、後続の PR で対応します。
-
事前作業を行わない場合(Direct)。
cacheComponentsを有効にし、ビルドで最初に指摘された箇所からループを始めます。ループは同じですが、移行が完了するまで、すべての修正をひとつのブランチに保持します。
どちらの場合も、ルートごとの成功条件は同じです。dev ループでエラーが報告されず、かつ next build が成功することです。機能をひとつ完了するごとにユーザーに状況を確認し、コミットを提案してください。ただし、確認を得ずにコミットしてはいけません。事前作業ではなく、ループの中で大半の時間を使うことを想定してください。
背景
cacheComponents: true では、すべてのルートを事前レンダリングできる必要があります。<Suspense> の外でリクエスト時のデータを読み取るルートは「ブロッキング」となり、ビルドに失敗します。export const instant = false を指定すると、そのルートはブロックしてもよいことが示され、開発環境とビルドの両方でこの問題が解消されます。レイアウトに指定した場合、ビルド時にはその配下全体に適用されますが、クライアントナビゲーションでは引き続き、各子孫セグメントが個別に検証されます。"use cache" 関数でラップされた読み取りは、ブロッキング読み取りではなく、キャッシュ境界として扱われます。
修正で "use cache" を導入する場合は、キャッシュガイドに従って、データレベルまたは UI レベルの境界を選択し、その有効期間を設定し、変更後に再検証してください。結果が引数やキャプチャされた値によって変わる場合は、use cache のキャッシュキーに関するリファレンスを参照してください。
ブロッカーは通常、次の順に3種類発生します。
修正で <Suspense> を導入する場合は、ストリーミングガイドの粒度の細かいストリーミングパターンとCLS を防ぐためのガイダンスに従ってください。
- リクエスト時の読み取り(
cookies()、headers()、await params、await searchParams)。ページやレイアウトの最上位で await すると、この4つはすべてブロックします。paramsとsearchParamsは、cookies や headers のように「リクエストデータ」として捉えられていないため、見落とされがちです。修正するには、読み取りを<Suspense>でラップした子コンポーネントに移します。params/searchParamsの場合は、Promise を子コンポーネントに渡し、そこで await してください。ページの最上位でawaitしてはいけません。 - モジュール/レンダー時の同期 I/O(
new Date()、Date.now()、Math.random()、crypto.randomUUID())。これらはinstant = falseを指定してもビルドに失敗します。このオプトアウトでは抑制されません。共有レイアウトにある場合、その配下のすべてのルートをブロックします。codemod では修正できません。実行後、各ビルドエラーと、そこにリンクされたドキュメントを使って、報告された呼び出しのうち手動で変換する必要があるものを特定してください(段階的な事前手順を参照)。 - リクエストデータを読み取る
"use cache"ファイル。ファイルの最上位に"use cache"ディレクティブがある場合、instantはエクスポートできません。この2つを組み合わせるとOnly async functions are allowed to be exported in a "use cache" file.というエラーになります。これは、そのルートに対するディレクティブが誤っていたことを意味します。codemod を実行する前に削除してください。
対応箇所
ブロックするルートを見つける
作業中は、next build より next dev を優先してください。
next dev— 作業の中心です。ルートにアクセスすると、そのルートをブロックするエラーが開発用オーバーレイに完全なスタックトレースとともに表示され、エラーごとのドキュメントにリンクする修正カードも表示されます。一度に一つのルートだけを作業してください。エラーが一か所に蓄積されることはありません。ルート自体は引き続き HTTP 200 を返すため、ステータスコードではなく、オーバーレイ(または.next-dev.log)を確認してください。オーバーレイがクリアされたことは、ルートに問題がないと判断するための条件の半分です。もう半分はブラウザーでの検証(ステップ 2を参照)と、そのルートのビルドが成功することです。next build— 検出専用です。ビルドはnext devによる権威あるチェックであり、代替ではありません。ループ内で各機能の最後のゲートとして(ビルドの成功はルートごとの成功条件に含まれます)、またアプリ全体の最終検証として使用してください。Incremental では、PR を出す前に、ビルドで事前ステップ(codemod によってすべてのルートがオプトアウトされ、共有レイアウトに同期 IO のブロッカーが残っていないこと)も確認できます。ルートの作業中に、開発ループの代わりとしてビルドを使わないでください。コンパイルに成功しても、静的シェルに何が含まれ、何がストリーミングされたかは分かりません。デフォルトでは、ビルドは最初にブロックするルートで停止するため、作業量を見積もる用途にも適していません。反復作業には、2 つのフラグが役立ちます。--debug-build-pathsは指定したルートだけをビルドします(プロジェクトルートからのファイルパスに対する、カンマ区切りの glob パターンです。例:--debug-build-paths="app/admin/**/page.tsx"— URL パスではありません。--debug-build-paths="app/(marketing)/about/page.tsx"—/aboutではありません。--debug-build-paths="app/admin"は何にも一致せず、ルートを 0 件ビルドしても警告は表示されません)。また、--debug-prerenderは早期終了を無効にするため、ビルドは最初のプリレンダリング失敗後も続行し、ブロックするルートをすべて報告して、発生元のファイルと行を示す、より詳細なスタックトレースを出力します。
ブロックするエラーごとにドキュメントページがあります。開いて確認してください。開発用オーバーレイとビルドターミナルのどちらにも、各エラーとともに https://nextjs.org/docs/messages/<slug> のリンクが表示されます。そのページが修正方法を示す公式の手順であり、インラインメッセージは概要です。パターンを知っていると思っていても、遭遇したエラーの種類ごとにリンクを取得してください。手順は更新されますし、同じエラーの種類でも、ルートが何を読み取るかによって適切な修正方法は異なる場合があります。インラインメッセージだけから判断して即興で修正しないでください。(/docs/messages/* ページはオフライン用に同梱されていません。ネットワークに接続できない場合は、node_modules/next/dist/docs/ にある API ごとのガイドを参照し、報告時にその制約を明記してください。)
実行時に各修正を検証する
ビルドに成功したり、オーバーレイが消えたりしても、ルートが実際に正しく動作する証拠にはなりません。Cache Components は実行時に関わる機能(静的シェルとストリーミングされるデータ)です。最後にまとめてではなく、修正のたびに検証してください。
優先順位は次のとおりです。
-
next-dev-loop— 強く推奨します。/_next/mcpとagent-browserを使った実際のブラウザーを照合し、コンパイル時と実行時の問題を一度に検出します。診断情報(React ツリー、サスペンス境界、コンソール、ネットワーク)は、手動でnext devを調べるよりも充実しています。ループを開始する前にインストールしてください。
next devだけでは説明できない問題に直面するまで待たないでください。このスキルと一緒に提供されるため、まず利用可能かどうかを確認し、利用できない場合にのみインストールしてください。npx skills add https://github.com/vercel/next.js/tree/canary/skills/next-dev-loopこのスキルには、必要な
agent-browserのバージョンが記載されており、インストール手順も案内されています。Turbopack が必要です。
package.jsonのdevスクリプトに--webpackが渡されている場合は、そのことをユーザーに伝え、webpack を使い続ける理由があるか確認してください。理由がなければ、Turbopack(Next.js 16.3 以降のデフォルト)に切り替えてください。webpack を使い続ける場合は、このインストールを省略し、代わりにビルドのみのループを使ってください。next-dev-loop自体をインストールする許可を得る必要はありません。開発用の依存関係をインストールするのと同じツールです。ユーザーがいる場合は、検証のためにインストールすることを簡潔に伝えてください。非対話型の実行環境(CI、ダッシュボード、サンドボックス)では、確認を求めずにインストールしてください。「ユーザーに確認できない」は、省略する理由になりません。省略が正当化されるのは、ネットワークがない、npm がない、ファイルシステムが読み取り専用、新しい依存関係を追加しない方針が明示されている、または開発スクリプトが webpack 専用であるなど、実際の技術的な障害がある場合のみです。省略する場合は、最終報告で具体的な障害を示してください。 -
自分で操作できるブラウザー。 Playwright、
agent-browserの直接利用、その他のブラウザー自動化ツールを使います。next-dev-loopが本当に使用不能な場合に限ってください。フレームワーク側のチェック(/_next/mcp)は実行できないため、DOM のアサーションだけではすべてのリグレッションを検出できません。「検証済み」とする際は、より慎重に判断してください。 -
ビルドのみ。 開発サーバーをまったく実行できない場合、唯一の判断材料はビルドです。
<Suspense>がない○ (Static)ルートは、テストするストリーミング対象がないため、ビルドで完全に検証できます。◐ (Partial Prerender)ルートはシェルのみが検証された状態です。報告時にその旨を明記してください。 -
ツールがまったくない場合。 開発サーバー(またはビルド)を実行してもらい、結果を報告してもらうか、到達したマイルストーンを引き継いでください。
step 1: 戦略を選ぶ
作業量ではなく、希望する PR の形についてユーザーに尋ねてください。ユーザーとの会話では、内部ラベル(Incremental、Direct)は決して使わないでください。これらはあくまで内部の補助情報です。PR と機能の観点から尋ねます。例:「まず Cache Components を有効にし、すべてのルートをバリデーション対象外にする PR を作成してから、後続の PR で機能ごとに実際のルートへの適用を進めますか? それとも、すべてを 1 つのブランチで行いますか?」。小規模なアプリでも、段階的な方法には価値があります(レビューしやすいサイズの PR、取り消し可能、// TODO: Cache Components adoption マーカーは次回の作業キューにもなります)。ユーザーに代わって選ばないでください。
ユーザーに尋ねられない場合は、Incremental をデフォルトとし、その選択を記録してください。
依頼内で明示された選択に従ってください。ユーザーが段階的な移行を求めると同時に、移行の完了も求めている場合は、同じタスク内で残りのルートに進む前に、段階的なチェックポイントを確立して検証してください。「完了」は作業を止める地点を定めるものであり、選択した戦略を変えるものではありません。
- Incremental — 静かな事前ステップとループです。codemod を実行してすべてのページとレイアウトをバリデーション対象外にし、ビルドを通します。そこで停止してユーザーに確認し(事前ステップの終了を参照)、その後step 2 のループに進み、機能ごとに後続の PR としてリリースします。
- Direct — 事前ステップを省略します。
cacheComponentsを有効にし、すぐにstep 2 のループに進みます。ビルドを妨げるルートが作業キューになります。
incremental
codemod を実行する前に、app ディレクトリ内で ^export const (revalidate|dynamic|fetchCache) を grep し、一致する項目ごとに移行ガイドに従ってください。dynamic = 'force-static' または dynamic = 'error' を使用するルートには、この PR で完全に移行するよう印を付けてください。codemod は互換性のない設定を削除しません。
codemod は作業ツリーに変更があると実行を拒否します。関係のない作業を先にコミットするか stash するか、--force を渡して作業中の変更とともに編集を適用してください。よくある誤検知として、Next.js を最近アップグレードした場合、package.json とロックファイルにはすでに未コミットの変更があります。先にコミットしてください。
npx @next/codemod@latest cache-components-instant-false ./app
requiresで解決した app ディレクトリを渡してください。パスが間違っていてもエラーにはなりません。0 ok と報告して終了コード 0 で終了するため、ファイル数を確認し、ゼロの場合は app が適用済みなのではなく、実行失敗として扱ってください。
このディレクトリ配下のすべての {page,layout,default} ファイルに export const instant = false(// TODO: Cache Components adoption コメント付き)を挿入します。すでに instant を宣言しているファイル、および "use client" または "use server" の指定があるモジュールは対象外です。その後、cacheComponents: true を設定します。TODO コメントがループの作業キューになります。
codemod を利用できない場合(古い @next/codemod、サンドボックス環境、オフラインでの実行)は、手動で同じ処理を行ってください。app ディレクトリ内の {page,layout,default}.{js,jsx,ts,tsx} のうち、"use client" または "use server" の指定がなく、instant もまだ宣言していない各ファイルについて、次の内容をインポートの後に挿入します。
// TODO: Cache Components adoption. Refactor this route so this opt-out can be removed.
// See: https://nextjs.org/docs/app/guides/migrating-to-cache-components
export const instant = false
codemod がすべてのセグメントを対象外にするのは、ルートだけではありません。これは意図的なものです。設定は上から順に解決され、最初に明示された設定が優先されます。最も上位にある instant = false がサブツリー全体を決定します。各セグメントに対象外の指定を置くと、あるセグメントの指定を削除したとき、そのセグメントだけがバリデーションされます。子孫はそれぞれの指定が残るため、引き続きビルドを通過します。ルートだけを対象外にすると、ルートの指定を削除したとき、アプリ全体のバリデーションが一度に再び有効になります。
最も上位の対象外指定が優先されるため、上から順に削除してください(最初にルートレイアウトを削除し、その後下位へ進みます)。先祖に指定が残っている間は、リーフの対象外指定を削除しても何も起こりません。
以前は静的だったルートと、そのバリデーションを妨げる共有セグメントから対象外指定を削除してください。エラーを解決し、引き続きプリレンダリングされることを確認してください。キャッシュされたデータ呼び出しがあるだけでは、そのルートが静的なままである証明にはなりません。
次に、next build を実行して、codemod では対処できなかったブロッカーを洗い出します。証明となるのはビルドであり、codemod の実行結果ではありません。new Date() / Math.random() を直接呼び出す共有レイアウトは、オプトアウトに関係なく依然として失敗します(背景を参照)。通常のビルドが、呼び出し箇所を特定せずに同期 IO エラーを報告する場合は、そのルートを next build --debug-prerender --debug-build-paths="app/path/to/page.tsx" で再実行してください。報告される各同期 IO エラーについて、次のように対応します。
-
モジュール/レンダー時の同期 IO。 ビルド出力に含まれるルート、発生元のファイルと行、
/docs/messages/リンクを使用してエラーを特定します。必要に応じて、リポジトリ全体でnew Date()、Date.now()、Math.random()、crypto.randomUUID()を grep してください(app/**/layout.{js,jsx,ts,tsx}だけでなく、その読み取りがレイアウトからインポートされた任意のコンポーネントに存在する可能性があります)。報告されていない一致箇所は変更しないでください。リンク先のエラーページにある適切なオプションを適用し、ビルドをアンブロックするためだけに一時的に導入した境界の上に、次のコメントを追加します。// TODO: Cache Components adoption. Added to unblock the build: remove this boundary to re-trigger the error and review the documented options.これは codemod が書き込むコメントと同じ
TODO: Cache Components adoptionプレフィックスを共有しているため、チェックイン時の grep で両方が見つかります。境界を削除すると、修正カードとともにエラーが再び発生します。これは、ループ内でオプトアウトを削除するのと同じ動きです。
各修正の後、利用可能な場合はスコープ付きビルドを再実行し、その後 next build を再度実行して次のブロッカーを見つけます。通常のビルドが通るまで繰り返します。
ビルドが通ったら、すべての遅延ルートが依然としてオプトアウトでカバーされていること、および以前に静的だったルートをカバーする共有オプトアウトが存在しないことを確認します。アプリに以前に静的だったルートがなく、ルートレイアウトが遅延のままである場合は、それがオプトアウトを取得したことを確認します(grep -n "export const instant" <app dir>/layout.*)。ルートレイアウトは /_not-found のようなフレームワークルートを含むすべてのルートをレンダリングするため、見落とされていた場合は、手動で export const instant = false を追加します。
/_not-found のような合成ルートにはユーザーファイルが存在しません。それらがブロックする場合は、合成ルートではなくルートレイアウトのオプトアウトを修正します。クライアントコンポーネント("use client")はオプトアウトを取得しません(それらから instant をエクスポートするとビルドエラーになります)が、まれなブロッカーではありません。頻度の高いケースは、ルートレイアウトのナビやヘッダーにあるクライアントコンポーネントが usePathname()/useSearchParams() を呼び出す場合です。これは blocking-prerender-client-hook で すべての 動的ルートをブロックし、静的ルートはパス(プリレンダー時にパス名が判明している)を通過するため、動的セグメントに到達するまでそれが隠蔽されます。これは祖先データの修正ではありません。<Suspense> のレシピについては、エラーのドキュメントページに従ってください。クライアントルートが サーバー データでブロックする場合にのみ、そのデータを祖先で修正します。
事前ステップの終わり: チェックイン
インクリメンタルのみです。事前ステップが出荷可能な PR です。ステップ 2 を開始する前に、通過したチェックポイントを記録します。ユーザーが同じタスク内で完全な移行を続行するようすでに依頼していない限り、停止してチェックインします。ユーザーとはその言語で話してください。「インクリメンタル」やその他の内部的なラベルを口にせず、導入、PR、およびアプリが現在何をするかについて話してください。ユーザーに次のことを伝えます。
- 何をしたか: Cache Components を有効にし、codemod を実行し、以前に静的だったルートを移行し、残りのブロッカーを修正し、ビルドが通ることを確認しました。
- 何が変わったか: 以前に静的だったルートは依然としてプリレンダーされます。その他のページとレイアウトは
// TODO: Cache Components adoptionオプトアウトを保持しています。 - 何を確認すべきか: 以前に静的だったルートが完全にプリレンダーされ、プリフェッチ可能なままであり、遅延ルート上のリクエスト固有のデータがリクエスト固有のままであること。
- 質問: 「Cache Components をルートごとに導入し始める前に、これを独立した PR として開きますか? それともこのブランチで続けますか?」回答を待ちます。
ユーザーがすでに移行を完了するよう依頼している場合は、同じ質問を再度する代わりに、このチェックポイントを記録した後に続行します。
direct
cacheComponents: true を設定して、step 2 に進みます。ビルドをブロックしているルートが作業キューです。
step 2: 内側のループで、一度に1つの機能からオプトアウトを削除する
「機能」とは、app/settings/profile/** や app/posts/[slug]/** のような単一のプロダクト領域であり、app/dashboard/** のようなアプリ全体ではありません。次に進む前に、1つの機能を最初から最後まで完了させます。
機能内では、上から下へ(ページより先にレイアウトを、まずルートレイアウトを)進めます。子孫より先にレイアウトのオプトアウトを削除すると、そのレイアウト自体のブロッキング読み取りが明らかになります。(Direct の場合、削除するオプトアウトはありません。失敗する各ルートを修正してください。祖先に手書きのオプトアウトがあり、検証を隠している場合は、まずそれを削除します。)
作業の途中でビルドが通っても、レイアウトが問題ないとは限りません。子孫ページにオプトアウトが残っている状態でレイアウトのオプトアウトを削除しても、各ページが継承された検証を隠すため、ビルドは通り続けます。レイアウト自体のブロッキング読み取りが表面化するのは、その下に何も隠すものがなくなってからです。レイアウトの境界に達しただけで、機能の作業を完了としないでください。
ブラウザーに到達できない事情が本当にある場合を除き、ブラウザーを使ったループを使用してください。何を「ブラウザーが利用可能」とみなすか、またブラウザーのインストール方法については、next-dev-loop スキルを基準にしてください。
ループ、ブラウザーあり(推奨)
ルートごとに:
- オプトアウトを削除します(Incremental)。または、失敗しているルートを対象にします(Direct)。
- 開発環境で再読み込みします。オーバーレイが消えていますか? 消えていれば検証に進みます。まだ赤く表示されていますか? 修正します。
- 修正します。エラーにリンクされているドキュメントページ(
https://nextjs.org/docs/messages/<slug>)を取得し、そこに記載された手順を適用します。インラインオーバーレイのテキストは概要であり、正確な情報はドキュメントページを参照してください。 - ブラウザーで検証します。最初の描画で表示される内容が、シェルに意図したとおりであることを確認します。フォールバックのままになっていたり、空のシェルからすべてがストリーミングされていたりしないことを確認してください。
- 修正で共有コード(レイアウトやサイドバーコンポーネントなど)に変更を加えた場合は、兄弟ルートを再確認します。共有シェルの変更により、現在のルートが修正されても、兄弟ルートが壊れることがあります。
ループ、ビルドのみ(フォールバック)
ブラウザーを操作する手段がない場合に使用します。たとえば、CI やサンドボックス環境の場合、ユーザーが next dev を実行しておらず、こちらでも起動できない場合です。得られるシグナルは弱く、ビルドが通りルートがプリレンダリングされることは確認できますが、静的シェルとストリーミングのどちらに反映されたかは確認できません。
ルートごとに:
- オプトアウトを削除します(Incremental)。または、失敗しているルートを対象にします(Direct)。
--debug-build-paths app/<route>/**(そのルートのみ)または--debug-prerender(全体のビルド。ただし最初のエラー以降も続行)を指定して再ビルドします。ルートが通りましたか? 次に進みます。まだブロックされていますか? 修正します。- 修正します。エラーにリンクされているドキュメントページ(
https://nextjs.org/docs/messages/<slug>)を取得し、そこに記載された手順を適用します。 - 修正で共有コードに変更を加えた場合は、兄弟ルートを再確認します。
- 機能を引き継ぐ際に、そのルートをビルドのみで検証済みとして明記します。各
◐ルートは、機能の作業を完了する前に、ブラウザーでの確認が必要です。
ループのメモ
- 背景にある3種類のブロッカーは、既存のコードをその場で修正すると見落とされがちです。下流のフェッチ(
getThing(id))をキャッシュしても、ページ本文の先頭にあるawait paramsは解消されません。パラメーターのプロミスを<Suspense>でラップした子コンポーネントに渡してください。 - 判断が曖昧な場合は、エージェントが決めるのではなく、ユーザーに確認してください。どの修正が適切か確信が持てない場合、ブロッキングコードがセキュリティに関わるように見える場合、またはユーザーが意図的にルートをブロッキング状態にしておきたい可能性がある場合は、編集前に references/per-page-decisions.md を読んでください。質問する際は、
next-dev-loopのブラウザー引き継ぎを使ってルートを表示し、スクリーンショットを代替手段として用意してください。「このままブロッキング状態にしておきますか?」という質問は、ファイルパスを見るよりページを見ながらのほうが、ずっと答えやすくなります。 - コメントでリファクタリングの内容を説明しないでください。codemod(またはあなた)が残すコメントは、オプトアウト箇所の
// TODO: Cache Components adoptionと、ユーザーが既に書いているコメントだけです。各<Suspense>境界や"use cache"呼び出しの動作をコメントで説明する必要はありません。コードを見ればわかります。コードから理由が明らかでない場合(たとえば、理由を添えて意図的に Block する場合)に限り、コメントを残してください。 - 同じ機械的な修正が必要なルートが多くある場合は、まず代表的なルートを1つ選んで検証してください。その後、共通の手順で互いに重複しないルートのグループをまとめて修正し、共有のビルドとブラウザーチェックをまとめて実行してください。
機能に含まれるルートの ToDo リストを作成してください。機能内のすべてのルートに問題がなくなったら、ステップ3に進みます。
ステップ 3:機能を検証する
ユーザーに確認する前のチェックリスト:
next buildが、ルートをブロックするエラーなしで完了すること。- 機能内に単独の TODO が残っていないこと:
grep -rn "TODO: Cache Components adoption"を実行すると、codemod のオプトアウトコメントと、事前ステップで追加した sync-IO のブロック解除の両方が見つかります。残っているinstant = falseは、意図的に残された、理由が文書化された Block であること(コメントが理由を示す内容に書き換えられていること。詳しくは references/per-page-decisions.md → 「Block を残す場合」を参照)。残っているawait io()またはawait connection()は、事前ステップからの残骸ではなく、意図的に保持するものとしてレビュー済みであること。 - ブラウザーで各ルートを表示し、静的シェルが最初にレンダリングされ、すべての
<Suspense>フォールバックが実際のコンテンツに置き換わることを確認します。可能であれば、フォールバック(ストリーミング中)と最終表示の両方を記録し、ユーザーに見せられるストリーミング体験のデモを用意します。ストリーミングが速すぎて確認できない場合は、ブラウザーでネットワークをスロットリングします。 - データを更新できる新しいキャッシュを作成した場合は、変更後の確認を行い、次の読み取りで期待どおりのデータが返されることを確かめます。
- ランタイム検証に失敗した場合は、移行前のブランチ、またはオプトアウトを復元した状態で同じルートを再現します。すでに存在する失敗は、環境またはデータの問題であり、移行によるリグレッションではありません。
その後、ユーザーに確認します。事前ステップと同じルールです。ユーザーの言葉で話してください。「機能ごとのループ」などの内部向けの用語は避け、移行した機能と、ユーザーに何が見えるかを説明します。
- 実施内容:変更したルートと、各ルートでユーザーに見える結果(例:「記事ページでは、レイアウトを静的なままにして、スケルトンの背後で記事本文をストリーミングするようになりました」)。
- 変更点:削除したオプトアウト、追加したフォールバック、導入したキャッシュ境界。
- 説明するだけでなく、実際に見せます。
next-dev-loopのブラウザーハンドオフに従ってルートを操作し、静的シェル → フォールバック → 最終コンテンツの流れをユーザーに見せます。ライブでの提示ができない場合は、撮影した変更前・変更後のスクリーンショットを添付します。 - クリックして確認できる情報を提供します。機能のルートをまとめた簡潔な表を用意し、開く URL と確認する点(すぐにレンダリングされるもの、表示されるフォールバック、ストリーミングされる内容)を記載して、ユーザー自身が各ルートを検証できるようにします。
- 質問:「この機能を PR にして次へ進みますか、それともここで止めますか?」回答を待ちます。
単純な機能では、確認を省略できます。 機能の移行が // TODO: Cache Components adoption のオプトアウトを削除するだけで(<Suspense> を追加せず、'use cache' を導入せず、レンダリング順も変更しない場合)、ユーザーに見える違いはありません。そこで止まらず次の機能に進み、次に確認するときに簡単に触れてください。
すべての機能でこのループを実施し終えたら(残っているすべての instant = false の下に理由コメントがあり、grep -rln "TODO: Cache Components adoption" app の結果が何もない状態)、さらに体験を向上させたい場合は さらに読む を案内するか、そのまま完了としてリリースします。
ルートテーブルの記号
ƒ → ◐ が通常、採用の着地点です。◐ (Partial Prerender) は、静的シェルがプリレンダリングされ、リクエスト時のコンテンツがストリーミングされることを意味します。これは、cookies()、headers()、params、searchParams を読み取るあらゆるルートの目標状態です。ドキュメントに記載された回避策(たとえば、await connection() を使うレイアウト)でリクエスト時の処理を行う場合など、正当な理由があれば一部のルートは ƒ のままでもかまいません。そのページはもはや オプトアウト しているのではなく、実際に動的なのです。◐ を目指すだけのために回避策を削除しないでください。逆も同様です。instant = false にしても、ルートが ƒ になるわけではありません。この記号が示すのは、ルートがプリレンダリング時に何をするかであり、どの検証用オプションを公開しているかではありません。
◐ が示すのはシェルが存在することだけで、その中身ではありません。<Suspense> 境界の位置が高すぎると(たとえば、ページ本文全体を囲んでいたり、記事のコンテンツを <Suspense fallback={null}> で囲んでいたりすると)、表示されるコンテンツが静的シェルからストリーミングされるペイロードへ移ります。それでもビルド結果には ◐ と表示されます。何らかの シェルはプリレンダリングされているためです(多くの場合、フレームワークのマークアップを含む <html><body> だけです)。ルートテーブルではシェルの中身を確認できませんが、ブラウザーなら確認できます。シェルが空で、すべてがストリーミングされている場合は、<Suspense> 境界を実際に動的な値を読み取る箇所に近づけてください。
さらに詳しく
以下の作業は任意で、ドキュメントに記載されています。ユーザーにはリンクを案内し、次に取り組むものを選んでもらってください。このスキル内で手順を説明しないでください。
- インスタントなナビゲーションをさらに調査する — 採用が完了した後に行う任意のフォローアップであり、必須ではありません。ビルドが成功しても、それですべてではありません。開発環境では各ページの読み込み時にすべてのルートを検証し(ページの読み込みとクライアントナビゲーションの両方をシミュレートします)、ビルドの最初のエラーで終了する動作や、子孫によるシャドーイングで見逃された問題も検出します。Partial Prefetching を採用したくないユーザーには、インスタントなナビゲーションを実現するための、より小規模な方法として案内してください。Partial Prefetching(以下)を採用する場合も同様のループを実行し、これらの知見も活用できます。そのため、両方を推奨し、どちらか一方にするか、両方にするかはユーザーに選んでもらってください。実行するループは、このリファレンスに記載されています。
next-partial-prefetching-adoption— Partial Prefetching を採用するためのフォローアップスキルです。partialPrefetchingを有効にし、すべての<Link prefetch={true}>を判断表に照らして監査します(または、instant-link-prefetch-partialの知見をもとに、フラグをオフにしたまま段階的に採用します)。このスキルが Cache Components の導入を進めるのと同じ方法で手順を進めますが、知見は開発環境でのみ得られるため、ビルドのループではなく、ブラウザーでクリックしながら確認します。インスタントなナビゲーションの改善内容は、各ルートのうちシェルでどこまでプリフェッチできるかに直接関わるため、その後に推奨します。概念については、Partial Prefetching の採用ガイドをご覧ください。- E2E テストでリグレッションを防ぐ —
@next/playwrightのinstant()ヘルパーは、ナビゲーション直後に表示される UI をアサートするため、リグレッションを CI で検出できます。ルートがインスタントになったら導入を推奨してください。next-dev-loopは 現時点 で問題がないことを確認し、instant()テストはそれを維持します。 next-cache-components-optimizer— 各ルートの静的シェルを拡張し、より多くのページをプリレンダリングして、ストリーミングされる部分を減らす別個のスキルです。純粋な最適化であり、採用プロセスには含まれません。