sanity-live-cache-components について
コーディングエージェント向けに、Sanity LiveをNext.js 16以降のCache Componentsへ組み込むスキルです。Sanity固有のデータ取得、ドラフトモード、ライブ再検証を扱い、一般的なフレームワーク作業はNext.js公式スキルに委ねます。
主な機能
- 01Sanity LiveとCache Componentsの設定
- 02共有キャッシュ取得ヘルパー
- 03ドラフトモードとVisual Editingの接続
- 04コンテンツのライブ再検証
- 05移行と実行時検証のガイド
活用シーン
- 01Next.jsアプリへのSanity Live導入
- 02既存のSanityデータ取得方式の移行
- 03Presentation Toolでのドラフトプレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ sanity-io/ next-sanity --skill sanity-live-cache-componentsインストール後、Claude Code で/sanity-live-cache-componentsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
reference/dynamic-segments.mdlayouts.mdlive-helpers.mdthree-layer-pattern.md詳細
Sanity Live + Cache Components
next-sanity を cacheComponents: true に設定した Next.js 16 以降のアプリに組み込みます。データは、単一の共有 'use cache' 境界(cachedSanity)を通じて sanityFetch で取得します。ルートレイアウトの <SanityLive> は、Sanity Content Lake への EventSource 接続を介してキャッシュ済みコンテンツを再検証します。ドラフトモードを有効にすると、Visual Editing と Presentation Tool が完全にサポートされます。
コードを書く前に、関連するガイドを node_modules/next/dist/docs/ で確認してください(利用可能な場合)。ガイドとこのスキルが矛盾する場合は、このスキルに従ってください。
このスキルでは、Cache Components ガイド(node_modules/next/dist/docs/ にオフライン版が含まれています)で説明されている Cache Components の基本事項('use cache'、cacheLife、cacheTag、cookies/headers/params のルール)を理解していることを前提とします。Sanity に関する例外は、await draftMode() を 'use cache' の内部で使用できることだけです(ドラフトモードが有効な場合、Next.js はキャッシュを回避します。詳細は use cache のリファレンスを参照してください)。
このスキルの位置づけ
Next.js には、フレームワーク共通のワークフローを扱う公式スキルが用意されています(AI コーディングエージェント向けにプロジェクトを設定するを参照)。このスキルが扱うのは Sanity の範囲のみで、それ以外は公式スキルに委ねます。Next.js リポジトリからインストールしてください。
npx skills add vercel/next.js --skill next-dev-loop
npx skills add vercel/next.js --skill next-cache-components-adoption
npx skills add vercel/next.js --skill next-cache-components-optimizer
npx skills add vercel/next.js --skill next-partial-prefetching-adoption
公式スキルのルールが適用される場合(ブロッキングルートのトリアージ、<Suspense> の配置、ローディング UI の再利用、instant() の回帰テスト、リンクのプリフェッチ監査など)は、その指針に従ってください。ここから指針を導き直す必要はありません。
アプリを移行する際の推奨手順:
-
next-cache-components-adoption—cacheComponents: trueを有効にし、ルートごとにアプリのビルドが成功する状態にします。Sanity の範囲はこのスキルに任せるよう指示してください。next-cache-components-adoption Skill を使用して、このプロジェクトに Cache Components を導入してください。ドラフトモードの処理と、すべての
sanityFetch/<SanityLive>呼び出し箇所は sanity-live-cache-components スキルに委ねてください。Sanity のデータ取得をリファクタリングするのではなく、それらのルートではオプトアウト(export const instant = false)したままにしてください。 -
このスキル —
defineLiveとlive.tsヘルパーを設定し、sanityFetchの呼び出し箇所をリファクタリングしてperspective/stegaを正しく取得し、<SanityLive>/<VisualEditing>とドラフトモードを接続してから、保留していた Sanity ルートに残るオプトアウトを削除します。削除には、導入スキルのルートごとのループと成功基準(開発オーバーレイに問題がなく、ブラウザーで検証済みで、next buildが成功すること)を使用してください。このスキルが提供するのは Sanity 固有の修正で、ループの進め方は導入スキルが提供します。 -
以下のいずれか、または両方を任意で追加できます:
next-cache-components-optimizer— ルートの静的シェルを拡張し、@next/playwrightのinstant()テストで保護します。プロンプト:「next-cache-components-optimizer Skill を使用して、/<route>へのナビゲーションを瞬時にしてください。」next-partial-prefetching-adoption—partialPrefetchingを有効にし、<Link prefetch={true}>の使用状況を監査します。プロンプト:「next-partial-prefetching-adoption Skill を使用して、このプロジェクトに Partial Prefetching を導入してください。」
このスキルがどちらかを妨げることはありません。3 層パターンにより、公開ブランチのルートは完全に事前レンダリング可能な状態に保たれます。これは、これらのスキルが拡張し、プリフェッチする対象となるシェルそのものです。
cachedSanityを介してキャッシュされた Sanity コンテンツも、ランタイムプリフェッチの要件である「URL に依存するキャッシュ済みコンテンツ」を満たします。
この一連の作業では、next-dev-loop を使って、実行時に変更を検証してください。コンパイルに成功しても、静的シェルとストリーミングのどちらに何が含まれるかは確認できません。
前提条件
- プロジェクトに Next.js 16.3+ がインストールされていること(
package.jsonを確認するか、pnpm list next/npm ls nextを実行してください。pnpm view next versionは使用しないでください。インストール済みのバージョンではなく、レジストリの最新バージョンが表示されます)。next-sanityv13 は Next.js 16 をサポートしますが、このスキルと組み合わせて使う公式スキルには 16.3+ が必要です。 AGENTS.mdが存在すること。Next.js 16.3+ では、next devが(バンドルされたドキュメントをエージェントに案内する)ファイルを自動生成します。それより古いバージョンでは、ガイドに従ってください。- 次の環境変数が設定されていること:
NEXT_PUBLIC_SANITY_PROJECT_IDNEXT_PUBLIC_SANITY_DATASETSANITY_API_READ_TOKEN
- 埋め込み Sanity Studio の設定(
sanity.config.ts、sanity.cli.ts、sanity/以下のすべてのファイル)は変更不要です。このスキルが変更するのは Next.js アプリの範囲のみです。
参考ファイル
| ファイル | 読むタイミング |
|---|---|
| reference/live-helpers.md | client.ts / live.ts の全内容、cachedSanity、sanityFetch*、getDynamicFetchOptions の詳細 |
| reference/three-layer-pattern.md | searchParams のバリエーションを含む、page.tsx 向けの Page → Dynamic → Cached パターン |
| reference/layouts.md | layout.tsx 内でのノンブロッキングなデータ取得 |
| reference/dynamic-segments.md | 高パフォーマンスな [slug] ルート:loading.tsx と部分的な generateStaticParams、またはレイアウト内でのノンブロッキングな動的 params |
1. next-sanity@^13 をインストールする
npm install next-sanity@^13 --save-exact
既存の Sanity Live 設定の移行
アプリですでに defineLive を使用している場合、このスキルで行うのは書き換えではなくリファクタリングです。以下の 5 ステップの手順は引き続き適用されますが、次の点に注意してください。
client.tsまたはlive.tsが存在する場合、上書きしないでください。 不足しているオプションを追加します。既存のtokenとstega.*の設定は維持してください。詳しくは reference/live-helpers.md を参照してください。- コードベース内で、
sanityFetchの呼び出し箇所にあるハードコードされたperspective: 'published'とstega: falseを検索し、 3 層パターンとgetDynamicFetchOptionsを使ってperspective/stegaを取得するようにリファクタリングしてください。 generateStaticParams内のsanityFetch呼び出しを検索し、cachedSanityStaticParamsに置き換えてください。generateMetadata/sitemap.ts/opengraph-image.tsxなどにあるsanityFetch呼び出しを検索し、cachedSanityMetadataに置き換えてください。'use server'関数内に直接記述されたsanityFetch呼び出しを検索し、cachedSanityに置き換えてください。- ツリー内に
<SanityLive>と<VisualEditing>がそれぞれ 1 つだけあることを確認してください。 複数回のレンダリングは未定義動作になります。
このファイルの末尾にある「grep で検索するアンチパターン」セクションに、検索パターンが記載されています。
2. next.config.ts を設定する
cacheComponents を有効にし、cacheLife.default を sanity に設定して、デフォルトの再検証期間を 1 年(15 分ではなく)にします。sanityFetch はオンデマンド再検証向けに最適化されているため、時間ベースの再検証は必要ありません。
// next.config.ts
import type {NextConfig} from 'next'
import {sanity} from 'next-sanity/live/cache-life'
const nextConfig: NextConfig = {
cacheComponents: true,
cacheLife: {default: sanity},
}
export default nextConfig
3. defineLive を設定し、ヘルパーをエクスポートする
src/sanity/lib/client.ts と src/sanity/lib/live.ts を作成します。live.ts の中核となる部分は次のとおりです。
// src/sanity/lib/live.ts (excerpt)
export const {SanityLive, sanityFetch} = defineLive({
client,
serverToken: token,
browserToken: token,
strict: true,
})
// The app's one shared 'use cache' boundary. `sanityFetch` calls
// `cacheTag`/`cacheLife` internally but doesn't create the boundary —
// this wrapper provides it once, so callers don't add their own.
export const cachedSanity: StrictDefinedFetchType = async (options) => {
'use cache'
return sanityFetch(options)
}
client.ts、getDynamicFetchOptions、cachedSanityMetadata、cachedSanityStaticParams を含むファイル全体の内容と、各ヘルパーのガイダンスについては、reference/live-helpers.md を参照してください。
live.ts からエクスポートされるヘルパーは次のとおりです。
| ヘルパー | 使用箇所 |
|---|---|
cachedSanity | サーバー側でコンテンツを取得する場合のデフォルト:ページ、レイアウト、コンポーネント、サーバーアクション |
sanityFetch | 独自の 'use cache' を持つコンポーネント内のみ(レンダリングされた JSX もキャッシュします) |
cachedSanityMetadata | generateMetadata、generateViewport、sitemap.ts、robots.ts、opengraph-image.tsx など |
cachedSanityStaticParams | generateStaticParams のみ |
getDynamicFetchOptions | 'use cache' の境界の外で perspective/stega を解決する場合 |
SanityLive | ルートレイアウトで一度だけレンダリング |
4. ルートレイアウトで <SanityLive> をレンダリングする
<SanityLive> と <VisualEditing> はどちらも layout.tsx に配置し、page.tsx には配置しないでください。ツリー全体で、それぞれ最大1回だけレンダリングする必要があります。複数回レンダリングした場合の動作は未定義です。
defineLiveをstrict: true(推奨される設定)で構成する場合、includeDraftsは必須です。指定がない場合、TypeScript がエラーを通知します。ライブ再検証にドラフトが含まれるのはドラフトモードの場合のみとなるよう、includeDrafts={isDraftMode}を渡してください。- 移行時には、
<SanityLive>に既存のオプションのコールバックプロパティがあれば維持してください。対象はonError、onWelcome、onReconnectです。これらは一般にトースト/通知ヘルパーに接続されているため、何も通知せずに削除すると UX が低下します。
// src/app/layout.tsx
import {SanityLive} from '@/sanity/lib/live'
import {VisualEditing} from 'next-sanity/visual-editing'
import {draftMode} from 'next/headers'
export default async function RootLayout({children}: LayoutProps<'/'>) {
const {isEnabled: isDraftMode} = await draftMode()
return (
<html lang="en">
<body>
{children}
<SanityLive includeDrafts={isDraftMode} />
{isDraftMode && <VisualEditing />}
</body>
</html>
)
}
Sanity Studio を埋め込む場合
ルートで next-sanity/studio の NextStudio をマウントする場合(例:app/studio/[[...index]]/page.tsx)、<SanityLive> は埋め込み Studio と共有されないレイアウトに配置する必要があります。ルートグループを使用し、<SanityLive> を src/app/(website)/layout.tsx に配置して、アプリの残りの部分も src/app/(website) の配下に置いてください。
5. ページとレイアウトに3層パターンを適用する
静的に事前レンダリングするすべてのルートで、同じ構成を使用します。
Page/Layout (Layer 1: draftMode branch)
├── NOT draft mode → <CachedX perspective="published" stega={false} /> (no Suspense)
└── draft mode → <Suspense fallback={...}>
<DynamicX params={params} /> (Layer 2: awaits dynamic APIs)
└── <CachedX perspective={p} stega={s} /> (Layer 3: fetches via cachedSanity)
重要なルール:
- キャッシュ境界は
live.ts(cachedSanity)にあるため、通常、ルートファイルに'use cache'ディレクティブを付ける必要はありません。特に、最上位のPage/Layoutに'use cache'を付けてはいけません。これらはparams、searchParams、または(getDynamicFetchOptions経由で)cookies()を待機し、これらの動的 API は'use cache'内では使用できないためです。最上位の関数に'use cache'を追加するのが最もよくある失敗パターンです。TypeScript とランタイムの両方でエラーが発生します。 - Layer 3 で
cachedSanityを待機するだけで、ルート全体を静的シェルに事前レンダリングできます。公開ブランチでは<Suspense>は不要です。perspectiveとstegaは自動的にラッパーのキャッシュキーに含まれるため、公開コンテンツとドラフトコンテンツでキャッシュエントリーが共有されることはありません。 - 動的 API に触れるのは、Layer 2(
<Suspense>内でレンダリングされ、ドラフトモードでのみ動作)だけです。
編集するファイルに合ったリファレンスを選んでください。
- 静的な
params、またはgenerateStaticParamsに基づくparamsを使うpage.tsx→ reference/three-layer-pattern.md。 searchParamsやその他の動的 API を使うpage.tsx→ reference/three-layer-pattern.md のsearchParamsバリアント。- 独自のデータを取得する
layout.tsx→ reference/layouts.md。 loading.tsxと部分的なgenerateStaticParamsによる最適化が必要な 動的な[slug]ルート、またはブロックせずにparamsを処理する必要があるレイアウト → reference/dynamic-segments.md。
Sanity の動作確認
リファクタリングのたびに next-dev-loop を使用してください。ループの仕組みと成功基準は、公式スキルに記載されています。Sanity 固有で確認すべき点は次のとおりです。
- 公開ブランチ:ルートが完全に事前レンダリングされ(ビルドのルートテーブルで
◐または○)、<Suspense>のフォールバックが一瞬表示されることなくコンテンツがレンダリングされること。 - ドラフトモード:有効にするとドラフトコンテンツがストリーミングされ、
<VisualEditing>オーバーレイが表示され、Presentation Tool でパースペクティブを切り替えるとレンダリングされるコンテンツが変わること。 - ライブ更新:Studio で公開済みコンテンツを編集すると、再ビルドせずにルートが再検証されること(
<SanityLive>経由)。
避けるべきパターンを検索する
アプリを監査するときは、以下を検索してリファクタリングしてください。
- 共有コンポーネント内の
sanityFetch/cachedSanity呼び出しで、perspective: 'published'とstega: falseが一緒にハードコードされている → 3層パターンを使用し、getDynamicFetchOptions経由でperspective/stegaを取得します。(Layer 1 のドラフト以外の分岐で、リテラルのperspective="published" stega={false}props を渡すのは適切なパターンであり、違反ではありません。) - 本文が
'use server'で始まる関数内のsanityFetch(→cachedSanityに置き換えます(先にgetDynamicFetchOptionsでperspective/stegaを解決します)。 generateStaticParams内のsanityFetch(→cachedSanityStaticParamsに置き換えます。generateMetadata/generateViewport/sitemap.ts/robots.ts/opengraph-image.tsxなどにあるsanityFetch(→cachedSanityMetadataに置き換え、getDynamicFetchOptionsでperspectiveを解決します。- 独自の
'use cache'ディレクティブがないコンポーネント内のsanityFetch(→cachedSanityに置き換えます(レンダリングされた JSX をキャッシュする意図がある場合は、ディレクティブを追加します)。 loading.tsxを兄弟ファイルとして持たないpage.tsxまたはlayout.tsxの先頭で、await draftMode()の直後にawait getDynamicFetchOptions()がある → これらの動的 API 呼び出しを<Suspense>でラップした子コンポーネントに移動し、静的シェルを事前レンダリングできるようにします。- ツリー内で
<SanityLive>または<VisualEditing>が複数回レンダリングされている → 適切なレイアウト内でのレンダリングに統合します。