How to Upgrade to Next.js 16 Now That 15 Is Unsupported

Next.js 15 stopped getting security fixes on 21 October 2026. The breaking changes in 16, the codemods that fix most of them, and a safe order to upgrade in.

Ananya RaoAnanya RaoAuthor26 October 20265 min read 45 views
How to Upgrade to Next.js 16 Now That 15 Is Unsupported
In this article▾
  1. Why 21 October was the cut-off
  2. Check the floor before you touch any code
  3. Step 1: let the codemods do the dull part
  4. Step 2: the breaking changes that actually bite
  5. Synchronous request APIs are gone
  6. middleware.ts becomes proxy.ts, with one catch
  7. Turbopack is the default, and custom webpack config fails the build
  8. next/image defaults changed
  9. Smaller removals worth a search
  10. A realistic upgrade order

If your app still runs on Next.js 15, it stopped getting security fixes on 21 October 2026. That is the date endoflife.date lists for the end of security support on the 15.x line, whose last release was 15.5.28 on 14 October. Next.js 16 is now the only line in active support, with 16.4.2 out since 15 October. The upgrade is not a rewrite, but a few changes will break a build on the first try. Here they are, in the order you will hit them, with the fix for each.

Why 21 October was the cut-off

The Next.js support policy keeps the current major in Active LTS and moves the previous one to Maintenance LTS, which lasts two years from that version's first release and covers only critical bug fixes and essential security updates. Next.js 15 shipped on 21 October 2024, so its two years ran out this week.

In practice, nothing in your app breaks today. The risk is the next advisory. If a vulnerability lands that affects 15.x, there is no promise of a patch, and you would be upgrading under pressure instead of on your own schedule. So plan the move now, while it is boring.

Check the floor before you touch any code

Next.js 16 raises the minimum versions. From the official version 16 upgrade guide:

  • Node.js 20.9 or later. Node 18 is no longer supported.

  • TypeScript 5.1 or later.

  • Browsers: Chrome, Edge and Firefox 111+, Safari 16.4+.

If your CI image or hosting runtime is pinned to Node 18, fix that first. It is the cheapest failure to find early.

Step 1: let the codemods do the dull part

Create a branch and run the upgrade codemod:

npx @next/codemod@canary upgrade latest

It updates the Turbopack config location, moves you from next lint to the ESLint CLI, renames middleware to proxy, drops the unstable_ prefix from stabilised APIs, and removes experimental_ppr from pages and layouts.

It does not fix everything. If your code still reads params, searchParams, cookies(), headers() or draftMode() synchronously, run the second codemod too:

npx @next/codemod@canary next-async-request-api .

Then read the diff. Codemods leave comments where they could not decide, and those comments are your to-do list.

Step 2: the breaking changes that actually bite

Synchronous request APIs are gone

Next.js 15 made these APIs async but kept a temporary synchronous fallback. Version 16 removes the fallback. A page like this will no longer work:

export default function Page({ params }) {
  return <h1>{params.slug}</h1>
}

The fix is to make the component async and await the promise. Run npx next typegen to get the generated PageProps helper, which keeps the route parameters typed:

export default async function Page(props: PageProps<'/blog/[slug]'>) {
  const { slug } = await props.params
  return <h1>Blog Post: {slug}</h1>
}

The same applies to opengraph-image, icon and sitemap functions, whose params and id now arrive as promises. Generated OG images are easy to forget because nobody looks at them during development.

middleware.ts becomes proxy.ts, with one catch

The middleware file and export name are deprecated in favour of proxy. The rename is mechanical, but read this line in the guide twice: the edge runtime is not supported in proxy, which always runs on Node.js. If your middleware depends on the edge runtime, keep it as middleware for now rather than renaming it blindly. Config flags change name too, for example skipMiddlewareUrlNormalize becomes skipProxyUrlNormalize.

Turbopack is the default, and custom webpack config fails the build

Both next dev and next build now use Turbopack. If a webpack function exists in your config, the build fails on purpose. That function is sometimes added by a plugin, not by you. You have three options: migrate the config to Turbopack's options, build with next build --webpack while you migrate, or run Turbopack and ignore the webpack config. The middle option is the sensible short-term choice for most teams.

next/image defaults changed

These do not throw errors. They change what users see, which is worse, because nobody notices until a designer does.

  • images.qualities now defaults to [75]. Any other quality prop is coerced to the nearest allowed value, and direct /_next/image requests with an unsupported q return a 400.

  • minimumCacheTTL goes from 60 seconds to 4 hours.

  • 16 is removed from the default imageSizes.

  • Local images with query strings, such as /assets/photo?v=1, need a matching images.localPatterns entry with search.

  • Redirects are capped at 3, and optimising images from local IP addresses is blocked unless you set dangerouslyAllowLocalIP.

If you rely on several quality levels, say so explicitly:

const nextConfig: NextConfig = {
  images: {
    qualities: [50, 75, 100],
  },
}
  • next lint is removed and next build no longer lints. Add a separate lint step in CI or you will quietly stop linting.

  • serverRuntimeConfig and publicRuntimeConfig are removed. Use environment variables, with the NEXT_PUBLIC_ prefix for anything the browser needs.

  • AMP support is removed entirely.

  • Every parallel route slot now needs a default.js, or the build fails. One that returns null keeps the old behaviour.

  • revalidateTag('posts') now wants a second argument, a cache profile such as revalidateTag('posts', 'max'). Use the new updateTag in Server Actions when a user must see their own change immediately.

A realistic upgrade order

  1. Bump Node in CI and hosting, then merge that alone.

  2. On a branch, run both codemods and fix their comments.

  3. Run npx next typegen and get tsc clean.

  4. Run next build. If the webpack check fails, add --webpack to the build script for now and open a ticket to remove it.

  5. Click through pages with images, generated OG images and any parallel routes on a preview deploy.

  6. Ship, then remove the --webpack flag in a separate change.

Do not switch on the React Compiler or cacheComponents as part of this upgrade. The guide is clear that enabling cacheComponents is not a rename and can surface new build errors. Get onto 16 first; adopt the new features once you are there.

If you truly cannot upgrade this month, at least make sure you are on the final 15.5.x release and put a date on the move. Running an unsupported framework is a choice, and it should be a written-down one.

Ananya Rao

Written by

Ananya Rao

Keep reading

More from Ananya Rao

More from Ananya Rao

More in Web Development

Have a story of your own?

Publisha is free to start. Write with AI that keeps your voice, and publish in a click.