Cache Components support, cleaner APIs, and better error handling

Published: May 21, 2026

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 set cacheComponents: true in your next.config.ts file to use defineLive with 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.

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. revalidateSyncTags on <SanityLive> is replaced by action; sanityFetch no longer caches the internal sync-tag lookup when cacheComponents: false.
  • Removed the <SanityLive> props that were on by default: refreshOnFocus, refreshOnReconnect.
  • Removed the opt-in <SanityLive> / defineLive props: refreshOnMount, intervalOnGoAway, fetchOptions, stega; onGoAway signature changed (no longer receives interval).
  • Removed deprecated hooks: useDraftModePerspective, useIsLivePreview, useDraftModeEnvironment.
  • Removed deprecated tag option on sanityFetch and tag prop on <SanityLive>. Use requestTag instead.
  • Renamed next-sanity/live type 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."
        : ""
    }`
  );
};

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);

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, and useVisualEditingEnvironment no longer require both <SanityLive> and <VisualEditing> to be rendered in layout.tsx. Rendering <VisualEditing> alone is now enough
  • onRestart prop on <SanityLive>: calls router.refresh() by default; pass onRestart={false} to disable
  • onReconnect prop on <SanityLive>: logs to console by default; pass onReconnect={false} to disable
  • includeDrafts prop on <SanityLive>: override the automatic draft-mode detection when a browserToken is set
  • resolvePerspectiveFromCookies utility: exposes the same perspective resolution sanityFetch uses internally, useful for custom toolbars and cacheComponents: true boundaries
  • stega: true now works on the published perspective, enabling Visual Editing overlays on published content (useful for Vercel Content Link)
sanity.ioAll Systems Operational© Sanity 2026
Change site theme