Cache Components support, cleaner APIs, and better error handling
Published: May 21, 2026
next-sanity v13 ships first-class support for Next.js Cache Components (cacheComponents: true), cleans up long-deprecated APIs, and adds new escape hatches for error handling and connection lifecycle events.
Cache Components support
You can now pass cacheComponents: true to defineLive to opt in to Next.js Cache Components. This unlocks full 'use cache' support for sanityFetch, giving you fine-grained, tag-based cache invalidation that integrates directly with Sanity Live.
We've been running this in production for the past couple of months. sanity.io runs with cacheComponents: true, and sanity.io/docs runs with cacheComponents: false. Both have been solid.
The fastest way to adopt Cache Components is with the sanity-live-cache-components skill, which drives the migration with an AI agent. For best results, set up AGENTS.md first, then run:
npx skills add https://github.com/sanity-io/next-sanity --skill sanity-live-cache-components
pnpm dlx skills add https://github.com/sanity-io/next-sanity --skill sanity-live-cache-components
yarn dlx skills add https://github.com/sanity-io/next-sanity --skill sanity-live-cache-components
bunx skills add https://github.com/sanity-io/next-sanity --skill sanity-live-cache-components
Suggested prompt for your agent:
Use the /sanity-live-cache-components skill to migrate this app to use Cache Components. When verifying with `next dev`, test both draft mode enabled and draft mode disabled because each mode has different rendering rules. `next build --debug-prerender` is not sufficient to verify that draft mode works correctly.
Prefer to migrate manually? The Cache Components setup guide walks through each step in detail.
Breaking changes
See the v12 to v13 migration guide for full details and code snippets. The key changes are:
- The default cache-invalidation behavior changed.
revalidateSyncTagson<SanityLive>is replaced byaction;sanityFetchno longer caches the internal sync-tag lookup whencacheComponents: false. - Removed the
<SanityLive>props that were on by default:refreshOnFocus,refreshOnReconnect. - Removed the opt-in
<SanityLive>/defineLiveprops:refreshOnMount,intervalOnGoAway,fetchOptions,stega;onGoAwaysignature changed (no longer receivesinterval). - Removed deprecated hooks:
useDraftModePerspective,useIsLivePreview,useDraftModeEnvironment. - Removed deprecated
tagoption onsanityFetchandtagprop on<SanityLive>. UserequestTaginstead. - Renamed
next-sanity/livetype exports:DefinedSanityFetchType→DefinedFetchType,DefinedSanityLiveProps→DefinedLiveProps,DefineSanityLiveOptions→DefineLiveOptions.
What else is new
Customize or silence the welcome message with onWelcome
By default, <SanityLive> logs a welcome message to the console when the live event stream connects. Pass a custom onWelcome handler to replace it with your own logic, or pass onWelcome={false} to disable it entirely.
"use client";
import type { SanityLiveOnWelcome } from "next-sanity/live";
export const onWelcome: SanityLiveOnWelcome = (
event,
{ includeDrafts, waitFor }
) => {
console.info(
`<SanityLive${
includeDrafts ? " includeDrafts" : ""
}> is connected and listening for live events to ${
includeDrafts
? "all content including drafts and version documents in content releases"
: "published content"
}.${
waitFor === "function"
? " Events will be delayed until after a Sanity Function has processed them."
: ""
}`
);
};import { onWelcome } from "./client-functions";
import { SanityLive } from "@/sanity/lib/live";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
{children}
<SanityLive onWelcome={onWelcome} />
</>
);
}Error boundaries with retry via onError="throw"
The default behavior still logs errors with console.error (CORS errors use console.warn). Pass onError="throw" to throw errors during render so they can be caught by the Next.js unstable_catchError API, which supports unstable_retry for retrying the render. This lets you build rich error UIs. For example, a toast that offers a retry button when Sanity Live can't connect.
"use client";
import { isCorsOriginError } from "next-sanity/live";
import { unstable_catchError, type ErrorInfo } from "next/error";
import { useEffect } from "react";
import { toast } from "sonner";
function SanityLiveErrorBoundary(
_props: {},
{ error, unstable_retry }: ErrorInfo
) {
useEffect(() => {
let toastId: string | number | undefined;
if (isCorsOriginError(error)) {
const { addOriginUrl } = error;
toastId = toast.warning(`Sanity Live couldn't connect`, {
description: `${new URL(window.origin).host} is blocked by CORS policy`,
richColors: true,
duration: Infinity,
action: addOriginUrl
? { label: "Manage", onClick: (e) => { e.preventDefault(); window.open(addOriginUrl.toString(), "_blank"); } }
: { label: "Retry", onClick: () => unstable_retry() },
cancel: addOriginUrl
? { label: "Retry", onClick: () => unstable_retry() }
: undefined,
});
} else if (error instanceof Error) {
console.error(error);
toastId = toast.error(error.message, {
richColors: true,
duration: Infinity,
action: { label: "Retry", onClick: () => unstable_retry() },
});
} else {
console.error(error);
toastId = toast.error("Unknown error", {
description: "Check the console for more details",
richColors: true,
duration: Infinity,
action: { label: "Retry", onClick: () => unstable_retry() },
});
}
return () => { toast.dismiss(toastId); };
}, [error, unstable_retry]);
return null;
}
export default unstable_catchError(SanityLiveErrorBoundary);import SanityLiveErrorBoundary from "./SanityLiveErrorBoundary";
import { SanityLive } from "@/sanity/lib/live";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
{children}
<SanityLiveErrorBoundary>
<SanityLive onError="throw" />
</SanityLiveErrorBoundary>
</>
);
}Strict mode for staged Cache Components migrations
Enable strict: true in defineLive to split your Cache Components migration across two PRs: first update all sanityFetch and <SanityLive> call sites to satisfy the new requirements, then flip cacheComponents: true in next.config.ts and add 'use cache' to functions that call sanityFetch.
Other additions
usePresentationQuery,useIsPresentationTool, anduseVisualEditingEnvironmentno longer require both<SanityLive>and<VisualEditing>to be rendered inlayout.tsx. Rendering<VisualEditing>alone is now enoughonRestartprop on<SanityLive>: callsrouter.refresh()by default; passonRestart={false}to disableonReconnectprop on<SanityLive>: logs to console by default; passonReconnect={false}to disableincludeDraftsprop on<SanityLive>: override the automatic draft-mode detection when abrowserTokenis setresolvePerspectiveFromCookiesutility: exposes the same perspective resolutionsanityFetchuses internally, useful for custom toolbars andcacheComponents: trueboundariesstega: truenow works on the published perspective, enabling Visual Editing overlays on published content (useful for Vercel Content Link)