Upgrade Next.js 15 to 16: What Breaks on Sanity Sites and What It Costs
By Nayan Kyada · · 7 min read
Part of The Sanity + Next.js Guide
If you need to upgrade Next.js 15 to 16 on a site that pulls content from Sanity, the good news is that the Sanity side is usually the quiet part. The risk sits in caching, revalidation, middleware and third-party packages that have not caught up. This post covers what to check, what tends to break, what it costs to put the upgrade off, and how much effort to expect.
I will only describe changes I am confident about. Always read the official Next.js 16 upgrade guide for the exact list, because it is the source of truth and I would rather you trust it than my memory of a single flag.
What changed in Next.js 16, at a practical level
Three themes matter for a Sanity-backed site.
- Turbopack as the default bundler. Dev and build now go through Turbopack by default. If you have a custom webpack config, for example for SVG handling or aliasing, that is the first place a build can fail. Either port it or opt out explicitly while you plan the migration.
- A more explicit caching model. Next.js 16 builds on the caching direction started in the 15 line, with Cache Components and the use cache directive as the way to opt into caching. If your site relies on implicit fetch caching behaviour, you need to know exactly what your pages cache today. I cover the related rendering model in my Partial Prerendering write-up.
- React 19.x underneath. The App Router runs on current React 19 canary-derived features. Libraries that peer-depend on older React ranges will complain at install time.
There are also async request APIs. In 15 you could often get away with synchronous access to cookies, headers, params and searchParams because of a compatibility shim. In 16 you should assume everything is awaited. If you skipped that cleanup during the last upgrade, it will surface now.
Typical breakages on Sanity-backed sites
These are the failures I look for first, in rough order of how often they appear on sites like this.
1. Synchronous params and searchParams
Sanity sites lean on dynamic routes for slugs, so this hits almost every page file.
// app/blog/[slug]/page.tsx
// Broken pattern: synchronous access
export default async function Page({ params }: { params: { slug: string } }) {
const post = await client.fetch(POST_QUERY, { slug: params.slug })
return <Article post={post} />
}
// Fixed: params is a promise
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
const post = await client.fetch(POST_QUERY, { slug })
return <Article post={post} />
}The same applies to generateMetadata, which is easy to forget because it lives in the same file.
2. Revalidation and webhooks
If your Sanity webhook calls a route handler that triggers revalidateTag or revalidatePath, retest it end to end after the upgrade. Caching semantics are where upgrades quietly change behaviour: pages that used to refresh may serve stale content, or the reverse. I walk through a safe pattern in Sanity webhooks with ISR revalidation and HMAC verification, and the batching side in Sanity webhook revalidation: batching tags.
3. Draft mode and Presentation preview
Preview is the feature editors notice first when it breaks. Check the draft mode enable and disable routes, the token used for drafts, and that the Presentation tool still loads your site in its iframe. Make sure your next-sanity version declares support for Next.js 16 before you start; that single check saves hours.
4. Middleware and request handling
Sites with locale routing or redirect logic in middleware should read the 16 notes carefully, since Next.js has been reshaping how this layer is named and used. Test every redirect that matters for SEO, not just the homepage.
5. Third-party packages
The long tail: animation libraries, analytics wrappers, form packages, image helpers. Run the install, read the peer dependency warnings, and upgrade or replace anything that blocks. Do not force installs with legacy peer flags and move on; that is how a broken production build gets scheduled.
What staying behind costs
Nothing breaks the day a new major ships, which is why teams defer. The cost is cumulative.
- Security and bug fixes concentrate on current versions, so older majors fall behind over time.
- Newer versions of next-sanity, Sanity Studio and UI libraries start requiring newer Next.js and React, so you get pinned to old tooling.
- Hiring gets harder. Developers expect current App Router patterns, not shims for behaviour that was already deprecated.
- Each skipped major adds its own breaking changes to the eventual jump, and the work compounds rather than adds.
If you are on Pages Router or an older App Router major, the upgrade is really two projects. My Pages Router to App Router buyer's guide covers that first leg, and if your Studio is also old, the Sanity Studio v2 to v3 upgrade post covers the path to v6.
Upgrade effort by codebase size
These are planning ranges from general practice, not quotes. Your numbers depend on test coverage, custom config and dependency health.
| Codebase | Typical shape | Starting from 15 | Starting from 14 or older |
|---|---|---|---|
| Small | Marketing site, under 30 routes, one locale | 1 to 2 days | 2 to 4 days |
| Medium | 30 to 150 routes, webhooks, draft mode, a few integrations | 3 to 6 days | 1 to 2 weeks |
| Large | Multi-locale, custom middleware, heavy third-party code | 1 to 2 weeks | 3 to 5 weeks |
The biggest multiplier is not size. It is whether anyone knows what the site is supposed to do. Sites with no tests and no documentation take longer because every page must be checked by hand.
A sane upgrade path
- Branch off, and get a green baseline build on the current version first.
- Update Next.js, React and next-sanity together, then fix install warnings.
- Run the official codemods where Next.js provides them, then review the diff rather than trusting it blindly.
- Fix the build, then fix type errors. If you use Sanity TypeGen, regenerate types so query changes do not hide behind stale definitions.
- Test on a staging URL: preview, webhook-triggered updates, redirects, sitemap, robots and structured data.
- Compare Core Web Vitals before and after. A Turbopack or caching change should not hurt them, but verify instead of assuming.
What to ask a developer before they start
- Which Next.js and next-sanity versions will you target, and why?
- How will you confirm revalidation and draft mode still work after the change?
- What is the rollback plan if production regresses?
- Will you give me a staging URL and a checklist of tested routes?
A developer who answers with specifics has done this before. One who says it is just a version bump has not.
This is work I do. If your site is stuck on an older Next.js major and you want a scoped look first, my Performance and Sanity audit starts from $800, and the upgrade itself can be quoted from what the audit finds.
Frequently asked questions
01How long does it take to upgrade Next.js 15 to 16?
A small marketing site with a few dozen routes is usually a day or two including testing. A mid-size Sanity site with webhooks, draft mode and custom routes tends to take several days, and a large multi-locale codebase can take a couple of weeks.
02Will upgrading to Next.js 16 break my Sanity integration?
The Sanity client itself is framework-agnostic, so the queries rarely break. What breaks is the glue: caching behaviour, revalidation calls, draft mode routes, middleware and any dependency that has not declared support for the new React or Next.js version.
03Is it risky to stay on Next.js 15?
Not immediately, but older majors stop receiving fixes over time, and dependencies start requiring newer versions. Each major you skip makes the eventual upgrade larger, so it is cheaper to move one major at a time.
04Should I upgrade straight from Next.js 13 or 14 to 16?
Treat it as stepwise work even if you do it in one project. Fix the older breaking changes first, get the site green, then move to the current major, so you can tell which change caused which failure.