How to upgrade to version 16
Source URL: https://nextjs.org/docs/app/guides/upgrading/version-16
How to upgrade to version 16
Section titled “How to upgrade to version 16”Upgrading from 15 to 16
Section titled “Upgrading from 15 to 16”Using AI Agents with Next.js DevTools MCP
Section titled “Using AI Agents with Next.js DevTools MCP”If you’re using an AI coding assistant that supports the Model Context Protocol (MCP), you can use the Next.js DevTools MCP to automate the upgrade process and migration tasks.
Add the following configuration to your MCP client, for each coding agent you can read this section for configuration details.
example:
{ "mcpServers": { "next-devtools": { "command": "npx", "args": ["-y", "next-devtools-mcp@latest"] } }}For more information, visit the next-devtools-mcp documentation to configure with your MCP client.
Note: Using
next-devtools-mcp@latestensures that your MCP client will always use the latest version of the Next.js DevTools MCP server.
Example Prompts
Section titled “Example Prompts”Once configured, you can use natural language prompts to upgrade your Next.js app:
To upgrade to Next.js 16:
Connect to your coding agent and then prompt:
Next Devtools, help me upgrade my Next.js app to version 16To migrate to Cache Components (after upgrading to v16):
Connect to your coding agent and then prompt:
Next Devtools, migrate my Next.js app to cache componentsLearn more in the documentation here.
Using the Codemod
Section titled “Using the Codemod”To update to Next.js version 16, you can use the upgrade codemod:
pnpm dlx @next/codemod@canary upgrade latestnpx @next/codemod@canary upgrade latestyarn dlx @next/codemod@canary upgrade latestbunx @next/codemod@canary upgrade latestThe codemod is able to:
- Update
next.config.jsto use the newturbopackconfiguration - Migrate from
next lintto the ESLint CLI - Migrate from deprecated
middlewareconvention toproxy - Remove
unstable_prefix from stabilized APIs - Remove
experimental_pprRoute Segment Config from pages and layouts
If you prefer to do it manually, install the latest Next.js and React versions:
pnpm add next@latest react@latest react-dom@latestnpm install next@latest react@latest react-dom@latestyarn add next@latest react@latest react-dom@latestbun add next@latest react@latest react-dom@latestIf you are using TypeScript, ensure you also upgrade @types/react and @types/react-dom to their latest versions.
Node.js runtime and browser support
Section titled “Node.js runtime and browser support”| Requirement | Change / Details |
|---|---|
| Node.js 20.9+ | Minimum version now 20.9.0 (LTS); Node.js 18 no longer supported |
| TypeScript 5+ | Minimum version now 5.1.0 |
| Browsers | Chrome 111+, Edge 111+, Firefox 111+, Safari 16.4+ |
Turbopack by default
Section titled “Turbopack by default”Starting with Next.js 16, Turbopack is stable and used by default with next dev and next build
Previously you had to enable Turbopack using --turbopack, or --turbo.
{ "scripts": { "dev": "next dev --turbopack", "build": "next build --turbopack", "start": "next start" }}This is no longer necessary. You can update your package.json scripts:
{ "scripts": { "dev": "next dev", "build": "next build", "start": "next start" }}If your project has a custom webpack configuration and you run next build (which now uses Turbopack by default), the build will fail to prevent misconfiguration issues.
You have a few different ways to address this:
- Use Turbopack anyway: Run with
next build --turbopackto build using Turbopack and ignore yourwebpackconfig. - Switch to Turbopack fully: Migrate your
webpackconfig to Turbopack-compatible options. - Keep using Webpack: Use the
--webpackflag to opt out of Turbopack and build with Webpack.
Good to know: If you see failing builds because a
webpackconfiguration was found, but you don’t define one yourself, it is likely that a plugin is adding awebpackoption
Opting out of Turbopack
Section titled “Opting out of Turbopack”If you need to continue using Webpack, you can opt out with the --webpack flag. For example, to use Turbopack in development but Webpack for production builds:
{ "scripts": { "dev": "next dev", "build": "next build --webpack", "start": "next start" }}We recommend using Turbopack for development and production. Submit a comment to this thread, if you are unable to switch to Turbopack.
Turbopack configuration location
Section titled “Turbopack configuration location”The experimental.turbopack configuration is out of experimental.
import type { NextConfig } from 'next'
// Next.js 15 - experimental.turbopackconst nextConfig: NextConfig = { experimental: { turbopack: { // options }, },}
export default nextConfigYou can use it as a top-level turbopack option:
import type { NextConfig } from 'next'
// Next.js 16 - turbopack at the top level of nextConfigconst nextConfig: NextConfig = { turbopack: { // options },}
export default nextConfigMake sure to review the Turbopack configuration options. Next.js 16 introduces various improvements and new options, for example:
Resolve alias fallback
Section titled “Resolve alias fallback”In some projects, client-side code may import files containing Node.js native modules. This will cause Module not found: Can't resolve 'fs' type of errors.
When this happens, you should refactor your code so that your client-side bundles do not reference these Node.js native modules.
However, in some cases, this might not be possible. In Webpack the resolve.fallback option was typically used to silence the error. Turbopack offers a similar option, using turbopack.resolveAlias. In this case, tell Turbopack to load an empty module when fs is requested for the browser.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { turbopack: { resolveAlias: { fs: { browser: './empty.ts', // We recommend to fix code imports before using this method }, }, },}
export default nextConfigIt is preferable to refactor your modules so that client code doesn’t ever import from modules using Node.js native modules.
Sass node_modules imports
Section titled “Sass node_modules imports”Turbopack fully supports importing Sass files from node_modules. Note that while Webpack allowed the legacy tilde (~) prefix, Turbopack does not support this syntax.
In Webpack:
@import '~bootstrap/dist/css/bootstrap.min.css';In Turbopack:
@import 'bootstrap/dist/css/bootstrap.min.css';If changing the imports is not possible, you can use turbopack.resolveAlias. For example:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { turbopack: { resolveAlias: { '~*': '*', }, },}
export default nextConfigTurbopack File System Caching (beta)
Section titled “Turbopack File System Caching (beta)”Turbopack now supports filesystem caching in development, storing compiler artifacts on disk between runs for significantly faster compile times across restarts.
Enable filesystem caching in your configuration:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { experimental: { turbopackFileSystemCacheForDev: true, },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { experimental: { turbopackFileSystemCacheForDev: true, },}
module.exports = nextConfigAsync Request APIs (Breaking change)
Section titled “Async Request APIs (Breaking change)”Version 15 introduced Async Request APIs as a breaking change, with temporary synchronous compatibility.
Starting with Next.js 16, synchronous access is fully removed. These APIs can only be accessed asynchronously.
cookiesheadersdraftModeparamsinlayout.js,page.js,route.js,default.js,opengraph-image,twitter-image,icon, andapple-icon.searchParamsinpage.js
Use the codemod to migrate to async Dynamic APIs.
Migrating types for async Dynamic APIs
Section titled “Migrating types for async Dynamic APIs”To help migrate to async params and searchParams, you can run npx next typegen to automatically generate these globally available types helpers:
Good to know:
typegenwas introduced in Next.js 15.5
This simplifies type-safe migration to the new async API pattern, and enables you to update your components with full type safety, for example:
export default async function Page(props: PageProps<'/blog/[slug]'>) { const { slug } = await props.params const query = await props.searchParams return <h1>Blog Post: {slug}</h1>}This approach gives you fully type-safe access to props.params, including the slug, and to searchParams, directly within your page.
Async parameters for icon, and open-graph Image (Breaking change)
Section titled “Async parameters for icon, and open-graph Image (Breaking change)”The props passed to the image generating functions in
opengraph-image,twitter-image,icon, andapple-icon, are now Promises.
In previous versions, both the Image (image generation function), and the generateImageMetadata received a params object. The id returned by generateImageMetadata was passed as a string to the image generation function.
// Next.js 15 - synchronous params accessexport function generateImageMetadata({ params }) { const { slug } = params return [{ id: '1' }, { id: '2' }]}
// Next.js 15 - synchronous params and id accessexport default function Image({ params, id }) { const slug = params.slug const imageId = id // string // ...}Starting with Next.js 16, to align with the Async Request APIs change, the image generating function now receives params and id as promises. The generateImageMetadata function continues to receive synchronous params.
export async function generateImageMetadata({ params }) { const { slug } = params return [{ id: '1' }, { id: '2' }]}
// Next.js 16 - asynchronous params and id accessexport default async function Image({ params, id }) { const { slug } = await params // params now async const imageId = await id // id is now Promise<string> when using generateImageMetadata // ...}Async id parameter for sitemap (Breaking change)
Section titled “Async id parameter for sitemap (Breaking change)”Previously, the id values returned from generateSitemaps were passed directly to the sitemap generating function.
export async function generateSitemaps() { return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]}
// Next.js 15 - synchronous id accessexport default async function sitemap({ id }) { const start = id * 50000 // id is a number // ...}Starting with Next.js 16, the sitemap generating function now receives id as a promise.
export async function generateSitemaps() { return [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }]}
// Next.js 16 - asynchronous id accessexport default async function sitemap({ id }) { const resolvedId = await id // id is now Promise<string> const start = Number(resolvedId) * 50000 // ...}React 19.2
Section titled “React 19.2”The App Router in Next.js 16 uses the latest React Canary release, which includes the newly released React 19.2 features and other features being incrementally stabilized. Highlights include:
- View Transitions: Animate elements that update inside a Transition or navigation
useEffectEvent: Extract non-reactive logic from Effects into reusable Effect Event functions- Activity: Render “background activity” by hiding UI with
display: nonewhile maintaining state and cleaning up Effects
Learn more in the React 19.2 announcement.
React Compiler Support
Section titled “React Compiler Support”Built-in support for the React Compiler is now stable in Next.js 16 following the React Compiler’s 1.0 release. The React Compiler automatically memoizes components, reducing unnecessary re-renders with zero manual code changes.
The reactCompiler configuration option has been promoted from experimental to stable. It is not enabled by default as we continue gathering build performance data across different application types.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { reactCompiler: true,}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { reactCompiler: true,}
module.exports = nextConfigInstall the latest version of the React Compiler plugin:
pnpm add -D babel-plugin-react-compilernpm install -D babel-plugin-react-compileryarn add -D babel-plugin-react-compilerbun add -D babel-plugin-react-compilerGood to know: Expect compile times in development and during builds to be higher when enabling this option as the React Compiler relies on Babel.
Caching APIs
Section titled “Caching APIs”revalidateTag
Section titled “revalidateTag”revalidateTag has a new function signature. You can pass a cacheLife profile as the second argument.
'use server'
import { revalidateTag } from 'next/cache'
export async function updateArticle(articleId: string) { // Mark article data as stale - article readers see stale data while it revalidates revalidateTag(`article-${articleId}`, 'max')}'use server'
import { revalidateTag } from 'next/cache'
export async function updateArticle(articleId) { // Mark article data as stale - article readers see stale data while it revalidates revalidateTag(`article-${articleId}`, 'max')}Use revalidateTag for content where a slight delay in updates is acceptable, such as blog posts, product catalogs, or documentation. Users receive stale content while fresh data loads in the background.
updateTag
Section titled “updateTag”updateTag is a new Server Actions-only API that provides read-your-writes semantics, where a user makes a change and the UI immediately shows the change, rather than stale data.
It does this by expiring and immediately refreshing data within the same request.
'use server'
import { updateTag } from 'next/cache'
export async function updateUserProfile(userId: string, profile: Profile) { await db.users.update(userId, profile)
// Expire cache and refresh immediately - user sees their changes right away updateTag(`user-${userId}`)}'use server'
import { updateTag } from 'next/cache'
export async function updateUserProfile(userId, profile) { await db.users.update(userId, profile)
// Expire cache and refresh immediately - user sees their changes right away updateTag(`user-${userId}`)}This ensures interactive features reflect changes immediately. Perfect for forms, user settings, and any workflow where users expect to see their updates instantly.
Learn more about when to use updateTag or revalidateTag here.
refresh
Section titled “refresh”refresh allows you to refresh the client router from within a Server Action.
'use server'
import { refresh } from 'next/cache'
export async function markNotificationAsRead(notificationId: string) { // Update the notification in the database await db.notifications.markAsRead(notificationId)
// Refresh the notification count displayed in the header refresh()}'use server'
import { refresh } from 'next/cache'
export async function markNotificationAsRead(notificationId) { // Update the notification in the database await db.notifications.markAsRead(notificationId)
// Refresh the notification count displayed in the header refresh()}Use it when you need to refresh the client router after performing an action.
cacheLife and cacheTag
Section titled “cacheLife and cacheTag”cacheLife and cacheTag are now stable. The unstable_ prefix is no longer needed.
Wherever you had aliased imports like:
import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag,} from 'next/cache'You can update your imports to:
import { cacheLife, cacheTag } from 'next/cache'Enhanced Routing and Navigation
Section titled “Enhanced Routing and Navigation”Next.js 16 includes a complete overhaul of the routing and navigation system, making page transitions leaner and faster. This optimizes how Next.js prefetches and caches navigation data:
- Layout deduplication: When prefetching multiple URLs with a shared layout, the layout is downloaded once.
- Incremental prefetching: Next.js only prefetches parts not already in cache, rather than entire pages.
These changes require no code modifications and are designed to improve performance across all apps.
However, you may see more individual prefetch requests with much lower total transfer sizes. We believe this is the right trade-off for nearly all applications.
If the increased request count causes issues, please let us know by creating an issue or discussion item.
Partial Pre-Rendering (PPR)
Section titled “Partial Pre-Rendering (PPR)”Next.js 16 removes the experimental Partial Pre-Rendering (PPR) flag and configuration options, including the route level segment experimental_ppr.
Starting with Next.js 16, you can opt into PPR using the cacheComponents configuration.
/** @type {import('next').NextConfig} */const nextConfig = { cacheComponents: true,}
module.exports = nextConfigPPR in Next.js 16 works differently than in Next.js 15 canaries. If you are using PPR today, stay in the current Next.js 15 canary you are using. We will follow up with a guide to migrate to Cache Components.
/** @type {import('next').NextConfig} */const nextConfig = { // If you are using PPR today // stay in the current Next.js 15 canary experimental: { ppr: true, },}
module.exports = nextConfigmiddleware to proxy
Section titled “middleware to proxy”The middleware filename is deprecated, and has been renamed to proxy to clarify network boundary and routing focus.
The edge runtime is NOT supported in proxy. The proxy runtime is nodejs, and it cannot be configured. If you want to continue using the edge runtime, keep using middleware. We will follow up on a minor release with further edge runtime instructions.
# Rename your middleware filemv middleware.ts proxy.ts# ormv middleware.js proxy.jsThe named export middleware is also deprecated. Rename your function to proxy.
export function proxy(request: Request) {}export function proxy(request) {}We recommend changing the function name to proxy, even if you are using a default export.
Configuration flags that contained the middleware name are also renamed. For example, skipMiddlewareUrlNormalize is now skipProxyUrlNormalize
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { skipProxyUrlNormalize: true,}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { skipProxyUrlNormalize: true,}
module.exports = nextConfigThe version 16 codemod is able to update these flags too.
next/image changes
Section titled “next/image changes”Local Images with Query Strings (Breaking change)
Section titled “Local Images with Query Strings (Breaking change)”Local image sources with query strings now require images.localPatterns.search configuration to prevent enumeration attacks.
import Image from 'next/image'
export default function Page() { return <Image src="/assets/photo?v=1" alt="Photo" width="100" height="100" />}If you need to use query strings with local images, add the pattern to your configuration:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { images: { localPatterns: [ { pathname: '/assets/**', search: '?v=1', }, ], },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { images: { localPatterns: [ { pathname: '/assets/**', search: '?v=1', }, ], },}
module.exports = nextConfigminimumCacheTTL Default (Breaking change)
Section titled “minimumCacheTTL Default (Breaking change)”The default value for images.minimumCacheTTL has changed from 60 seconds to 4 hours (14400 seconds). This reduces revalidation cost for images without cache-control headers.
For some Next.js users, image revalidation was happening frequently, often because the upstream source images missed a cache-control header. This caused revalidation to happen every 60 seconds, which increased CPU usage and cost.
Since most images do not change often, this short interval is not ideal. Setting the default to 4 hours offers a more durable cache by default, while still allowing images to update a few times per day if needed.
If you need the previous behavior, change minimumCacheTTL to a lower value, for example back to 60 seconds:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { images: { minimumCacheTTL: 60, },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { images: { minimumCacheTTL: 60, },}
module.exports = nextConfigimageSizes Default (Breaking change)
Section titled “imageSizes Default (Breaking change)”The value 16 has been removed from the default images.imageSizes array.
We have looked at request analytics and found out that very few projects ever serve 16 pixels width images. Removing this setting reduces the size of the srcset attribute shipped to the browser by next/image.
If you need to support 16px images:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { images: { imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { images: { imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], },}
module.exports = nextConfigRather than lack of developer usage, we believe 16 pixels width images have become less common, because devicePixelRatio: 2 actually fetches a 32px image to prevent blurriness in retina displays.
qualities Default (Breaking change)
Section titled “qualities Default (Breaking change)”The default value for images.qualities has changed from allowing all qualities to only [75].
If you need to support multiple quality levels:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { images: { qualities: [50, 75, 100], },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { images: { qualities: [50, 75, 100], },}
module.exports = nextConfigIf you specify a quality prop not included in the image.qualities array, the quality will be coerced to the closest value in images.qualities. For example, given the configuration above, a quality prop of 80, is coerced to 75.
Local IP Restriction (Breaking change)
Section titled “Local IP Restriction (Breaking change)”A new security restriction blocks local IP optimization by default. Set images.dangerouslyAllowLocalIP to true only for private networks.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { images: { dangerouslyAllowLocalIP: true, // Only for private networks },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { images: { dangerouslyAllowLocalIP: true, // Only for private networks },}
module.exports = nextConfigMaximum Redirects (Breaking change)
Section titled “Maximum Redirects (Breaking change)”The default for images.maximumRedirects has changed from unlimited to 3 redirects maximum.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = { images: { maximumRedirects: 0, // Disable redirects // or maximumRedirects: 5, // Increase for edge cases },}
export default nextConfig/** @type {import('next').NextConfig} */const nextConfig = { images: { maximumRedirects: 0, // Disable redirects // or maximumRedirects: 5, // Increase for edge cases },}
module.exports = nextConfignext/legacy/image Component (deprecated)
Section titled “next/legacy/image Component (deprecated)”The next/legacy/image component is deprecated. Use next/image instead:
// Beforeimport Image from 'next/legacy/image'
// Afterimport Image from 'next/image'images.domains Configuration (deprecated)
Section titled “images.domains Configuration (deprecated)”The images.domains config is deprecated.
// image.domains is deprecatedmodule.exports = { images: { domains: ['example.com'], },}Use images.remotePatterns instead for improved security:
// Use image.remotePatterns insteadmodule.exports = { images: { remotePatterns: [ { protocol: 'https', hostname: 'example.com', }, ], },}Concurrent dev and build
Section titled “Concurrent dev and build”next dev and next build now use separate output directories, enabling concurrent execution. The next dev command outputs to .next/dev. This is the new default behavior, controlled by isolatedDevBuild.
Additionally, a lockfile mechanism prevents multiple next dev or next build instances on the same project.
Since the development server outputs to .next/dev, the Turbopack tracing command should be:
pnpm next internal trace .next/dev/trace-turbopacknpx next internal trace .next/dev/trace-turbopackyarn next internal trace .next/dev/trace-turbopackbunx next internal trace .next/dev/trace-turbopackParallel Routes default.js requirement
Section titled “Parallel Routes default.js requirement”All parallel route slots now require explicit default.js files. Builds will fail without them.
To maintain previous behavior, create a default.js file that calls notFound() or returns null.
import { notFound } from 'next/navigation'
export default function Default() { notFound()}Or return null:
export default function Default() { return null}ESLint Flat Config
Section titled “ESLint Flat Config”@next/eslint-plugin-next now defaults to ESLint Flat Config format, aligning with ESLint v10 which will drop legacy config support.
Make sure to review our API reference for the @next/eslint-plugin-next plugin.
If you’re using the legacy .eslintrc format, consider migrating to the flat config format. See the ESLint migration guide for details.
Scroll Behavior Override
Section titled “Scroll Behavior Override”In previous versions of Next.js, if you had set scroll-behavior: smooth globally on your <html> element via CSS, Next.js would override this during SPA route transitions, as follows:
- Temporarily set
scroll-behaviortoauto - Perform the navigation (causing instant scroll to top)
- Restore your original
scroll-behaviorvalue
This ensured that page navigation always felt snappy and instant, even when you had smooth scrolling enabled for in-page navigation. However, this manipulation could be expensive, especially at the start of every navigation.
In Next.js 16, this behavior has changed. By default, Next.js will no longer override your scroll-behavior setting during navigation.
If you want Next.js to perform this override (the previous default behavior), add the data-scroll-behavior="smooth" attribute to your <html> element:
export default function RootLayout({ children }) { return ( <html lang="en" data-scroll-behavior="smooth"> <body>{children}</body> </html> )}Performance Improvements
Section titled “Performance Improvements”Significant performance optimizations for next dev and next start commands, along with improved terminal output with clearer formatting, better error messages, and improved performance metrics.
Next.js 16 removes the size and First Load JS metrics from the next build output. We found these to be inaccurate in server-driven architectures using React Server Components. Both our Turbopack and Webpack implementations had issues, and disagreed on how to account for Client Components payload.
The most effective way to measure actual route performance is through tools such as Chrome Lighthouse or Vercel Analytics, which focus on Core Web Vitals and downloaded resource sizes.
next dev config load
Section titled “next dev config load”In previous versions the Next config file was loaded twice during development:
- When running the
next devcommand - When the
next devcommand started the Next.js server
This was inefficient because the next dev command doesn’t need the config file to start the Next.js server.
A consequence of this change is that, when running next dev checking if process.argv includes 'dev', in your Next.js config file, will return false.
Good to know: The
typegen, andbuildcommands, are still visible inprocess.argv.
This is specially important for plugins that trigger side-effects on next dev. If that’s the case, it might be enough to check if NODE_ENV is set to development.
import { startServer } from 'docs-lib/dev-server'
const isDev = process.env.NODE_ENV === 'development'
if (isDev) { startServer()}
const nextConfig = { /* Your config options */}
module.exports = nextConfigAlternatively, use the phase in which the configuration is loaded.
Build Adapters API (alpha)
Section titled “Build Adapters API (alpha)”Following the Build Adapters RFC, the first alpha version of the Build Adapters API is now available.
Build Adapters allow you to create custom adapters that hook into the build process, enabling deployment platforms and custom build integrations to modify Next.js configuration or process build output.
const nextConfig = { experimental: { adapterPath: require.resolve('./my-adapter.js'), },}
module.exports = nextConfigShare your feedback in the RFC discussion.
Modern Sass API
Section titled “Modern Sass API”sass-loader has been bumped to v16, which supports modern Sass syntax and new features.
Removals
Section titled “Removals”These features were previously deprecated and are now removed:
AMP Support
Section titled “AMP Support”AMP adoption has declined significantly, and maintaining this feature adds complexity to the framework. All AMP APIs and configurations have been removed:
ampconfiguration from your Next config filenext/amphook imports and usage (useAmp)
// Removedimport { useAmp } from 'next/amp'
// Removedexport const config = { amp: true }export const config = { amp: true }from pages
const nextConfig = { // Removed amp: { canonicalBase: 'https://example.com', },}
export default nextConfigEvaluate if AMP is still necessary for your use case. Most performance benefits can now be achieved through Next.js’s built-in optimizations and modern web standards.
next lint Command
Section titled “next lint Command”The next lint command has been removed. Use Biome or ESLint directly. next build no longer runs linting.
A codemod is available to automate migration:
pnpm dlx @next/codemod@canary next-lint-to-eslint-cli .npx @next/codemod@canary next-lint-to-eslint-cli .yarn dlx @next/codemod@canary next-lint-to-eslint-cli .bunx @next/codemod@canary next-lint-to-eslint-cli .The eslint option in the Next.js config file is also removed.
/** @type {import('next').NextConfig} */const nextConfig = { // No longer supported // eslint: {},}
export default nextConfigRuntime Configuration
Section titled “Runtime Configuration”serverRuntimeConfig and publicRuntimeConfig have been removed. Use environment variables instead.
Before (Next.js 15):
module.exports = { serverRuntimeConfig: { dbUrl: process.env.DATABASE_URL, }, publicRuntimeConfig: { apiUrl: '/api', },}import getConfig from 'next/config'
export default function Page() { const { publicRuntimeConfig } = getConfig() return <p>API URL: {publicRuntimeConfig.apiUrl}</p>}After (Next.js 16):
For server-only values, access environment variables directly in Server Components:
async function fetchData() { const dbUrl = process.env.DATABASE_URL // Use for server-side operations only return await db.query(dbUrl, 'SELECT * FROM users')}
export default async function Page() { const data = await fetchData() return <div>{/* render data */}</div>}Good to know: Use the taint API to prevent accidentally passing sensitive server values to Client Components.
For client-accessible values, use the NEXT_PUBLIC_ prefix:
NEXT_PUBLIC_API_URL="/api"'use client'
export default function ClientComponent() { const apiUrl = process.env.NEXT_PUBLIC_API_URL return <p>API URL: {apiUrl}</p>}To ensure environment variables are read at runtime (not bundled at build time), use the connection() function before reading from process.env:
import { connection } from 'next/server'
export default async function Page() { await connection() const config = process.env.RUNTIME_CONFIG return <p>{config}</p>}Learn more about environment variables.
devIndicators Options
Section titled “devIndicators Options”The following options have been removed from devIndicators:
appIsrStatusbuildActivitybuildActivityPosition
The indicator itself remains available.
experimental.dynamicIO
Section titled “experimental.dynamicIO”The experimental.dynamicIO flag has been renamed to cacheComponents:
Update your Next config file, by removing the dynamicIO flag.
// Next.js 15 - experimental.dynamicIO is now removedmodule.exports = { experimental: { dynamicIO: true, },}Add the cacheComponents flag set to true.
// Next.js 16 - use cacheComponents insteadmodule.exports = { cacheComponents: true,}