# Next.js 16 and SanityLive: avoiding request overages

If you're using `SanityLive` in a Next.js app, upgrading to Next.js 16 can cause a large increase in requests and ISR writes compared to Next.js 15. That increase can drive up Sanity API usage and Vercel ISR costs. This article covers what to expect and how to work around it while a fix is in progress.

**This applies if:** you're running a Next.js App Router app, using `next-sanity`'s `defineLive` and `<SanityLive>`, and you're upgrading from Next.js 15 to 16 (or have already upgraded).

> [!WARNING]
> Stay on Next.js 15 until next-sanity v12 ships
> We recommend staying on Next.js 15 with `next-sanity` v11. Don't upgrade to Next.js 16 until `next-sanity` v12 is released. v12 is currently being tested under the `next-sanity@cache-components` tag and is running in production on sanity.io (with cache components enabled) and sanity.io/docs (on v16 with cache components disabled). Status as of April 2026.

## What to expect

With Next.js 16 and `<SanityLive>`, the default `<Link>` prefetch behavior combined with how `<SanityLive>` calls `revalidateTag` produces a cascade: each prefetch fires more requests than on v15, a live event invalidates the client-side cache, prefetches run again, and routes that match the revalidated tag re-fetch and write to ISR.

In production we've seen an average **4x** increase in request load from the same app after upgrading. Worst cases (marketing and docs sites with many segments) are **7–10x** or more.

### How to tell if this is affecting you

- Your app is on Next.js 16 with `<SanityLive>` in use.
- Sanity API request counts jumped 4x or more after the upgrade, with no comparable change in traffic.
- Vercel ISR writes spiked on routes that consume Sanity content.
- Browser devtools show multiple `?rsc` requests fired for each `<Link>` prefetch.

## Safest way to run SanityLive on Next.js 16

If downgrading to Next.js 15 isn't an option and you're not using cache components, this is the safest setup today:

1. Upgrade to at least Next.js 16.2 and [enable](https://nextjs.org/blog/next-16-2#experimentalprefetchinlining) `experimental.prefetchInlining`.
2. Keep using `sanityFetch` from `defineLive` in production, but don't render `<SanityLive>` unless Presentation Tool or visual editing is active.
3. Use a [sync tag function](https://www.sanity.io/docs/functions/sync-tag-function-quickstart) and set up an `/api/expire-tags` handler in your Next.js app that calls `revalidateTag(tag, "max")`. Then call this route from the sync tag function.
4. When you do render `<SanityLive>` (per step 2, for Presentation Tool or visual editing), render it only in draft mode and override `revalidateSyncTags` so it no longer calls `revalidateTag(tag, { expire: 0 })`.

**app/layout.tsx**

```tsx
<>
  {isDraftMode && <SanityLive revalidateSyncTags={refreshAction} />}
</>
```

The `refreshAction` implementation must live in a file marked `'use client'` (not `'use server'`) and return `'refresh'`. That tells `<SanityLive>` to call `router.refresh()` for you, which triggers a single GET that live-updates the page.

**app/actions/refresh.ts**

```typescript
'use client'

export async function refreshAction(): Promise<'refresh'> {
  return 'refresh'
}
```

## If you need cache components enabled

This combination is **not supported** on `next-sanity@latest`. Don't use `defineLive` on Next.js 16 with cache components. A [manual setup is possible](https://github.com/sanity-io/lcapi-examples/pull/356), but it does not support Presentation Tool's content releases perspective switching (previewing scheduled releases alongside published content in the live preview).

For draft-mode previewing, pass `includeDrafts` to the live events stream, call `router.refresh()` from draft mode, and toggle the fetch perspective based on draft state. The snippets below illustrate the pattern across multiple files in your app:

```typescript
// Subscribe to draft events when draft mode is active
client.live.events({ includeDrafts: true })

// In your data fetcher
client.fetch(query, params, {
  perspective: isDraftMode ? 'drafts' : 'published',
})

// When an event arrives in draft mode
router.refresh()
```

## Why Next.js 16 without cache components is risky

This combination *is* supported on `next-sanity@latest`, but it's considered dangerous and leads to overage risk. The default prefetch behavior on `<Link>` tags changed in v16 (see Next.js's "[Incremental prefetching](https://nextjs.org/blog/next-16#core-features--architecture)"). Combined with `<SanityLive>` calling `revalidateTag(tag, { expire: 0 })`, the result is:

- Each `<Link>` prefetch fires many more requests than in v15. On sanity.io we observed 2 requests with `?rsc` on v15 versus 7 on v16.
- `<SanityLive>` receives a live event and calls `revalidateTag(tag, { expire: 0 })`. That server action nukes the Next.js client-side cache (`router.refresh()` has the same effect).
- Clearing the client cache empties the prefetch cache. Next.js sees the visible Link tags and prefetches them all again.
- If any of those prefetches hit a route tagged with the revalidated tag, Next.js responds `CACHE: REVALIDATED` (its internal signal that a tagged route was re-rendered), which performs a data fetch (counts toward Sanity API usage) and triggers an ISR write (billable on Vercel).

Next.js's core team has confirmed the prefetch request change. The `CACHE: REVALIDATED` behavior on prefetch links is still under investigation. We know it happens, but not yet why v16 behaves differently from v15.

## Where the fix is tracked

Updates are landing in `next-sanity` v12 under the `next-sanity@cache-components` tag. Watch the [next-sanity GitHub releases](https://github.com/sanity-io/next-sanity/releases) for the public v12 announcement.

## Related articles

- [Visual Editing with Next.js App Router](https://www.sanity.io/docs/visual-editing/visual-editing-with-next-js-app-router)
- [Add live content to your application](https://www.sanity.io/docs/developer-guides/live-content-guide)
- [Live Content API](https://www.sanity.io/docs/content-lake/live-content-api)

