sanity-live-cache-components
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSanity Live + Cache Components
Sanity Live + Cache Components
Wires into a Next.js 16+ app with . Data is fetched with (which calls / internally), and in the root layout revalidates cached content over an EventSource connection to Sanity Content Lake. Visual Editing and Presentation Tool are fully supported when draft mode is enabled.
next-sanitycacheComponents: truesanityFetchcacheTagcacheLife<SanityLive>Read the relevant guide in (when available) before writing code. If a guide conflicts with this skill, follow this skill.
node_modules/next/dist/docs/This skill assumes familiarity with the skill — it covers , , , and the cookies/headers/params rule. The only Sanity-relevant exception: is allowed inside (Next.js bypasses caching when draft mode is enabled — see the reference).
next-cache-components'use cache'cacheLifecacheTagawait draftMode()'use cache'use cache将接入启用的Next.js 16+应用。通过(内部调用/)获取数据,根布局中的通过与Sanity Content Lake的EventSource连接重新验证缓存内容。启用草稿模式后,Visual Editing和Presentation Tool将获得全面支持。
next-sanitycacheComponents: truesanityFetchcacheTagcacheLife<SanityLive>在编写代码前,请先阅读中的相关指南(若可用)。若指南与本技能内容冲突,请以本技能为准。
node_modules/next/dist/docs/本技能假定使用者已熟悉技能——涵盖、、以及cookies/headers/params规则。与Sanity相关的唯一例外:允许在内部使用(Next.js会在启用草稿模式时绕过缓存——详见参考文档)。
next-cache-components'use cache'cacheLifecacheTagawait draftMode()'use cache'use cachePrerequisites
前提条件
- Next.js 16.2+ installed in the project (check or run
package.json/pnpm list next— don't usenpm ls next, that reports the registry's latest, not what's installed).pnpm view next version - exists, or follow the guide.
AGENTS.md - These environment variables are set:
NEXT_PUBLIC_SANITY_PROJECT_IDNEXT_PUBLIC_SANITY_DATASETSANITY_API_READ_TOKEN
- Embedded Sanity Studio configuration (,
sanity.config.ts, anything undersanity.cli.ts) needs no changes — this skill only touches the Next.js app surface.sanity/
- 项目中已安装Next.js 16.2+(查看或运行
package.json/pnpm list next— 不要使用npm ls next,该命令会显示注册表中的最新版本,而非已安装版本)。pnpm view next version - 存在,或遵循相关指南。
AGENTS.md - 已设置以下环境变量:
NEXT_PUBLIC_SANITY_PROJECT_IDNEXT_PUBLIC_SANITY_DATASETSANITY_API_READ_TOKEN
- 嵌入式Sanity Studio配置(、
sanity.config.ts、sanity.cli.ts下的所有文件)无需修改——本技能仅涉及Next.js应用层面的改动。sanity/
Reference files
参考文件
| File | When to read |
|---|---|
| reference/live-helpers.md | Full |
| reference/three-layer-pattern.md | The Page → Dynamic → Cached pattern for |
| reference/layouts.md | Non-blocking data fetching inside |
| reference/dynamic-segments.md | High-performance |
| 文件 | 阅读时机 |
|---|---|
| reference/live-helpers.md | 完整的 |
| reference/three-layer-pattern.md | |
| reference/layouts.md | 在 |
| reference/dynamic-segments.md | 高性能 |
1. Install next-sanity@^13
next-sanity@^131. 安装next-sanity@^13
next-sanity@^13bash
npm install next-sanity@^13 --save-exactbash
npm install next-sanity@^13 --save-exactMigrating an existing Sanity Live setup
迁移现有Sanity Live配置
If the app is already using , this skill is a refactor, not a rewrite. The 5-step sequence below still applies, but watch for these specific differences:
defineLive- Don't overwrite or
client.tsif they exist. Append missing options. Preserve any existinglive.tsandtokensettings — see reference/live-helpers.md.stega.* - Search the codebase for hardcoded and
perspective: 'published'instega: falsecallsites and refactor them to sourcesanityFetch/perspectiveviastegaand the three-layer pattern.getDynamicFetchOptions - Search for calls inside
sanityFetch→ swap forgenerateStaticParams.sanityFetchStaticParams - Search for calls inside
sanityFetch/generateMetadata/sitemap.ts/ etc. → swap foropengraph-image.tsx.sanityFetchMetadata - Search for calls directly inside a
sanityFetchfunction → split into a separate'use server'helper.'use cache' - Verify there is exactly one and one
<SanityLive>in the tree. Multiple renders are undefined behavior.<VisualEditing>
The "Anti-patterns to grep for" section at the bottom of this file lists the search patterns.
若应用已在使用,本技能属于重构而非重写。以下5步流程仍然适用,但需注意以下差异:
defineLive- 若或
client.ts已存在,不要覆盖。追加缺失的配置项。保留现有live.ts和token设置——详见reference/live-helpers.md。stega.* - 在代码库中搜索硬编码的和
perspective: 'published',并重构为通过stega: false和三层模式获取getDynamicFetchOptions/perspective。stega - 搜索中的
generateStaticParams调用→替换为sanityFetch。sanityFetchStaticParams - 搜索/
generateMetadata/sitemap.ts等文件中的opengraph-image.tsx调用→替换为sanityFetch。sanityFetchMetadata - 搜索直接在函数内的
'use server'调用→拆分为独立的sanityFetch助手。'use cache' - 确保树中仅存在一个和一个
<SanityLive>。多次渲染会导致未定义行为。<VisualEditing>
本文底部的“需要排查的反模式”部分列出了具体搜索模式。
2. Configure next.config.ts
next.config.ts2. 配置next.config.ts
next.config.tsEnable and set to so default revalidation is 1 year (instead of 15 minutes). is optimized for on-demand revalidation and doesn't need time-based revalidation.
cacheComponentscacheLife.defaultsanitysanityFetchts
// 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启用并将设置为,使默认重新验证周期为1年(而非15分钟)。针对按需重新验证优化,无需基于时间的重新验证。
cacheComponentscacheLife.defaultsanitysanityFetchts
// 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 nextConfig3. Configure defineLive
and export helpers
defineLive3. 配置defineLive
并导出助手
defineLiveCreate and . The minimal call:
src/sanity/lib/client.tssrc/sanity/lib/live.tsdefineLivets
// src/sanity/lib/live.ts (excerpt)
export const {SanityLive, sanityFetch} = defineLive({
client,
serverToken: token,
browserToken: token,
strict: true,
})Full file contents (including , , , ) and per-helper guidance: reference/live-helpers.md.
client.tsgetDynamicFetchOptionssanityFetchMetadatasanityFetchStaticParamsThe helpers exported from :
live.ts| Helper | Used in |
|---|---|
| |
| |
| |
| Resolving |
| Rendered once in a root layout |
创建和。最小化调用如下:
src/sanity/lib/client.tssrc/sanity/lib/live.tsdefineLivets
// src/sanity/lib/live.ts (节选)
export const {SanityLive, sanityFetch} = defineLive({
client,
serverToken: token,
browserToken: token,
strict: true,
})完整文件内容(包括、、、)及各助手的使用指南:reference/live-helpers.md。
client.tsgetDynamicFetchOptionssanityFetchMetadatasanityFetchStaticParamslive.ts| 助手名称 | 使用场景 |
|---|---|
| 从 |
| |
| 仅在 |
| 在 |
| 在根布局中渲染一次 |
4. Render <SanityLive>
in a root layout
<SanityLive>4. 在根布局中渲染<SanityLive>
<SanityLive><SanityLive><VisualEditing>layout.tsxpage.tsx- is required when
includeDraftsis configured withdefineLive(the recommended setup). TypeScript will surface the error if it's missing; passstrict: trueso live revalidation includes drafts only in draft mode.includeDrafts={isDraftMode} - Preserve any existing optional callback props on when migrating:
<SanityLive>,onError,onWelcome. They are commonly wired to a toast/notification helper and silently dropping them regresses UX.onReconnect
tsx
// 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>
)
}<SanityLive><VisualEditing>layout.tsxpage.tsx- 当配置为
defineLive(推荐配置)时,strict: true是必填项。若缺失,TypeScript会提示错误;传入includeDrafts,使实时重新验证仅在草稿模式下包含草稿内容。includeDrafts={isDraftMode} - 迁移时保留上已有的可选回调属性:
<SanityLive>、onError、onWelcome。这些属性通常与提示/通知助手关联,若删除会导致用户体验退化。onReconnect
tsx
// 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>
)
}With an embedded Sanity Studio
搭配嵌入式Sanity Studio
If a route mounts from (e.g. ), must live in a layout the embedded studio doesn't share. Use route groups: put in and keep the rest of the app under .
NextStudionext-sanity/studioapp/studio/[[...index]]/page.tsx<SanityLive><SanityLive>src/app/(website)/layout.tsxsrc/app/(website)若某个路由挂载了中的(例如),必须放在嵌入式工作室未共享的布局中。使用路由组:将放在中,其余应用内容放在下。
next-sanity/studioNextStudioapp/studio/[[...index]]/page.tsx<SanityLive><SanityLive>src/app/(website)/layout.tsxsrc/app/(website)5. Apply the three-layer pattern to pages and layouts
5. 为页面和布局应用三层模式
Every route that should be statically prerendered uses the same shape:
text
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: 'use cache')Critical rule: Only Layer 3 carries . The top-level / must not have — it awaits , , or (via ), and those dynamic APIs are forbidden inside . Layer 3 carrying is enough for the whole route to prerender into the static shell. Adding to the top-level function is the most common failure mode — TypeScript and the runtime will both complain.
'use cache'PageLayout'use cache'paramssearchParamscookies()getDynamicFetchOptions'use cache''use cache''use cache'Pick the right reference for the file you're editing:
- with static or
page.tsx-backed params → reference/three-layer-pattern.md.generateStaticParams - that uses
page.tsxor other dynamic APIs → thesearchParamsvariant in reference/three-layer-pattern.md.searchParams - that fetches its own data → reference/layouts.md.
layout.tsx - Dynamic route that needs the
[slug]+ partialloading.tsxoptimization, or a layout that needs non-blockinggenerateStaticParams→ reference/dynamic-segments.md.params
每个需要静态预渲染的路由都遵循以下结构:
text
页面/布局(第一层:草稿模式分支)
├── 非草稿模式 → <CachedX perspective="published" stega={false} /> (无Suspense)
└── 草稿模式 → <Suspense fallback={...}>
<DynamicX params={params} /> (第二层:等待动态API)
└── <CachedX perspective={p} stega={s} /> (第三层:'use cache')关键规则:仅第三层使用。顶层页面/布局不能使用——它需要等待、或(通过),而这些动态API在内部是被禁止的。第三层使用已足够让整个路由预渲染为静态外壳。在顶层函数中添加是最常见的错误模式——TypeScript和运行时都会报错。
'use cache''use cache'paramssearchParamscookies()getDynamicFetchOptions'use cache''use cache''use cache'根据正在编辑的文件选择对应的参考文档:
- 带有静态参数或支持参数的**
generateStaticParams** → reference/three-layer-pattern.md。page.tsx - 使用或其他动态API的**
searchParams** → reference/three-layer-pattern.md中的page.tsx变体。searchParams - 自行获取数据的**** → reference/layouts.md。
layout.tsx - 需要+ 部分
loading.tsx优化的动态generateStaticParams路由,或需要非阻塞[slug]的布局 → reference/dynamic-segments.md。params
Anti-patterns to grep for
需要排查的反模式
When auditing an app, search for these and refactor:
- and
perspective: 'published'hardcoded together in astega: falsecall → use the three-layer pattern, sourcesanityFetch/perspectiveviastega.getDynamicFetchOptions - directly inside a function whose body begins with
sanityFetch(→ split into a separate'use server'helper.'use cache' - inside
sanityFetch(→ swap forgenerateStaticParams.sanityFetchStaticParams - inside
sanityFetch(/generateMetadata/generateViewport/sitemap.ts/robots.tsetc. → swap foropengraph-image.tsxand resolvesanityFetchMetadataviaperspective.getDynamicFetchOptions - immediately followed by
await draftMode()at the top of aawait getDynamicFetchOptions()orpage.tsxwithout a siblinglayout.tsx→ move those dynamic-API calls into a child component wrapped inloading.tsxso the static shell can prerender.<Suspense> - More than one or
<SanityLive>rendered in the tree → consolidate to a single render in the right layout.<VisualEditing>
审核应用时,搜索以下内容并进行重构:
- 调用中同时硬编码
sanityFetch和perspective: 'published'→ 使用三层模式,通过stega: false获取getDynamicFetchOptions/perspective。stega - 直接在以开头的函数内调用
'use server'→ 拆分为独立的sanityFetch(助手。'use cache' - 中的
generateStaticParams→ 替换为sanityFetch(。sanityFetchStaticParams - /
generateMetadata/generateViewport/sitemap.ts/robots.ts等文件中的opengraph-image.tsx→ 替换为sanityFetch(,并通过sanityFetchMetadata解析getDynamicFetchOptions。perspective - 在或
page.tsx顶部连续调用layout.tsx和await draftMode(),且无同级await getDynamicFetchOptions()→ 将这些动态API调用移至包裹在loading.tsx中的子组件,以便静态外壳可以预渲染。<Suspense> - 组件树中渲染多个或
<SanityLive>→ 合并为在正确布局中单次渲染。<VisualEditing>