next-sanity v13.0.0: Cache Components support, cleaner APIs, and better error handling
v13.0.0
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
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.
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)
Fetched May 21, 2026


