| | |

Next.js 18 ▲ Upgrading — Version migration & canary channel management

⏱️ Estimated read: 10 min | |

Next.js upgrades are not a single command. They are a sequence of decisions: which version to target, which codemods to run, which breaking changes to fix manually, and whether to track the stable channel or the canary channel. The stable channel receives the release that next@latest points to. The canary channel receives every commit that passes the test suite, including features that are not yet stable and breaking changes that have not been announced. The two channels serve different purposes, and mixing them without a plan is how a project ends up running unreleased code in production.

This chapter covers the upgrade path from a stable version to the latest stable release, the canary channel’s role and its risks, the automated codemods that handle the mechanical migrations, and the breaking changes that require manual attention in the current major version.

Key point: The next upgrade command handles the version bump and runs the available codemods. It does not fix every breaking change. The middleware rename, the parallel route default.js requirement, and the removal of next lint are manual migrations that the codemod does not cover.


Why the two channels exist

The stable channel problem. A production application needs a version that has been tested, documented, and supported. The stable channel provides that. next@latest resolves to the most recent stable release, and the version guides document the breaking changes between major versions . A stable upgrade is a planned migration with a known set of changes.

The canary channel problem. New features are developed on the canary channel before they are promoted to stable. The canary release is published on every commit, and the documentation describes it as the channel where “features available in canary” are listed . The authentication APIs (forbidden, unauthorized, authInterrupts) are an example of features that are documented as canary-only . A project that adopts canary gets those features early, at the cost of instability and undocumented breaking changes.

The upgrade command problem. The next upgrade command was introduced in Next.js 16.1.0. Before that version, the upgrade path was the @next/codemod package. The current documentation describes both: pnpm next upgrade for 16.1.0 and later, and npx @next/codemod@canary upgrade latest for earlier versions . The command upgrades the package versions and runs the codemods that are available for the migration.


a. The stable upgrade path

The stable upgrade begins with the version check. The current major version requires Node.js 20.9 or later, TypeScript 5.1 or later, and React 19.2 or later . A project on an older Node.js version must upgrade the runtime before it can run the new Next.js version.

The upgrade command is the first step. It bumps next, react, react-dom, and eslint-config-next to the latest versions and runs the codemods.

# Next.js 16.1.0 and later
pnpm next upgrade

# Earlier versions
npx @next/codemod@canary upgrade latest

# With a coding agent
npx next@canary upgrade --agent=latest

The --agent variant is documented as the path for a coding agent to perform and verify the upgrade . It is not a replacement for reviewing the diff; it is a way to automate the mechanical parts.

The codemods handle the async request APIs. The params, searchParams, cookies(), headers(), and draftMode() are Promises in the current version, and the synchronous access that was temporarily allowed in the previous major version is removed . The codemod adds the await and updates the TypeScript types. The next typegen command generates the PageProps, LayoutProps, and RouteContext helpers that make the migration type-safe .

// Before
export default function Page({ params }) {
  const { slug } = params
}

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

The codemod does not cover every breaking change. The middleware rename to proxy.ts is manual. The parallel route default.js requirement is manual. The removal of next lint, the serverRuntimeConfig/publicRuntimeConfig options, and the AMP support are manual . The version guide documents the full list, and the upgrade is not complete until each item has been addressed.


b. The canary channel

The canary channel is installed with a package manager command that targets the canary tag. The documentation recommends being on the latest stable version and confirming that everything works before switching to canary .

pnpm add next@canary
npm i next@canary

The canary channel is not a version that a production application should track without a specific reason. The features listed as canary-only are not stable, and the breaking changes are not documented in the same way as the stable version guides. A project that adopts canary accepts the risk of an unreleased change breaking the build or the runtime.

The canary channel is appropriate for a project that needs a specific unreleased feature, or for a maintainer who is testing the next version before it is promoted. It is not appropriate for a project that simply wants the latest stable release, because next@latest already provides that.


c. The manual breaking changes

The current major version has several breaking changes that the codemod does not fix. Each requires a manual migration.

The middleware rename. middleware.ts is renamed to proxy.ts, and the exported function is renamed from middleware to proxy. The NextResponse API is unchanged. The Edge runtime is not supported in proxy.ts, so a project that depends on the Edge runtime must keep middleware.ts until the migration is complete . A deployment to Cloudflare via OpenNext must also keep middleware.ts, because OpenNext does not recognize proxy.ts .

The parallel route default. Parallel routes require a default.js file in each slot. Without it, the build fails. The file renders null when the slot has no active match .

// app/@modal/default.tsx
export default function Default() {
  return null
}

The next lint removal. The next lint command is removed. The project uses ESLint directly, and the eslint-config-next package provides the configuration. The migration is to add an ESLint configuration file and run eslint . instead of next lint .

The runtime config removal. serverRuntimeConfig and publicRuntimeConfig are removed. The migration is to use environment variables, with the NEXT_PUBLIC_ prefix for the values that need to be available on the client .

The image defaults change. The next/image defaults changed: the cache TTL increased to 4 hours, the imageSizes array shrank to 5 sizes, and the qualities array defaulted to a single value (75) . A project that relied on the old defaults can revert them in next.config.ts.


Complete Example Session

# ============================================
# PART 1: THE VERSION CHECK
# ============================================
node --version
# v20.9.0 or later is required.
# ============================================
# PART 2: THE STABLE UPGRADE
# ============================================
pnpm next upgrade

# Or, for versions before 16.1.0:
npx @next/codemod@canary upgrade latest
# ============================================
# PART 3: THE MANUAL MIGRATIONS
# ============================================
# Rename middleware.ts → proxy.ts
# Rename middleware → proxy
# Keep middleware.ts if Edge runtime or OpenNext is required.
// ============================================
// PART 4: THE PARALLEL ROUTE DEFAULT
// ============================================
// app/@modal/default.tsx
export default function Default() {
  return null
}
# ============================================
# PART 5: THE LINT MIGRATION
# ============================================
# Remove next lint
# Add eslint.config.mjs
npx eslint .
// ============================================
// PART 6: THE RUNTIME CONFIG MIGRATION
// ============================================
// Before: serverRuntimeConfig
// After: process.env.SECRET

// Before: publicRuntimeConfig
// After: process.env.NEXT_PUBLIC_API_URL
// ============================================
// PART 7: THE IMAGE DEFAULTS REVERT
// ============================================
// next.config.ts
const config = {
  images: {
    minimumCacheTTL: 60,
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
}
# ============================================
# PART 8: THE ASYNC PARAMS CODEMOD
# ============================================
npx @next/codemod@canary upgrade latest

# The codemod adds await to params, searchParams,
# cookies(), headers(), and draftMode().
# ============================================
# PART 9: THE CANARY INSTALL
# ============================================
pnpm add next@canary

# Only for a project that needs an unreleased feature.
# ============================================
# PART 10: THE BUILD VERIFICATION
# ============================================
pnpm build

# The build must succeed before the upgrade is complete.

The ten parts covered the version check, the stable upgrade, the manual migrations, the parallel route default, the lint migration, the runtime config migration, the image defaults, the async params codemod, the canary install, and the build verification.


Quick Reference

Upgrade commands

CommandUse case
pnpm next upgradeNext.js 16.1.0 and later
npx @next/codemod@canary upgrade latestEarlier versions
npx next@canary upgrade --agent=latestCoding agent
pnpm add next@canaryCanary channel

Version requirements

RequirementValue
Node.js20.9 or later
TypeScript5.1 or later
React19.2 or later
BrowsersChrome 111+, Safari 16.4+, Firefox 109+

Codemod coverage

MigrationCodemod
Async params/searchParamsYes
Async cookies()/headers()Yes
middleware → proxyNo
Parallel route default.jsNo
next lint removalNo
Runtime config removalNo

Canary features (current)

FeatureStatus
forbiddenCanary
unauthorizedCanary
authInterruptsCanary
forbidden.jsCanary

Best Practices

✅ Do This:

# Upgrade from a clean working tree
git commit -am "pre-upgrade checkpoint"                     # ✅
# Run the build after the upgrade
pnpm build                                                  # ✅
# Use the codemod for the async APIs
npx @next/codemod@canary upgrade latest                     # ✅
# Keep middleware.ts if OpenNext or Edge is required
# Rename to proxy.ts only when the deployment supports it    # ✅

❌ Don’t Do This:

# Track canary in production without a reason
pnpm add next@canary                                        # ❌
# Skip the manual migrations
# The build fails or the middleware is silently disabled    # ❌
# Assume the codemod fixes everything
# The rename and the default.js are manual                  # ❌

Common Pitfalls

PitfallWhy It HappensFix
Build fails after upgradeParallel route default.js missingAdd the file to each slot
Middleware is silently disabledRenamed to proxy.ts on OpenNextKeep middleware.ts until OpenNext supports it
Async params errorCodemod not run or not completeRun the codemod and next typegen
Lint command failsnext lint removedUse eslint .
Runtime config undefinedserverRuntimeConfig removedUse process.env
Image optimization changedNew defaultsRevert in next.config.ts

Real-World Examples

1. Stable upgrade

pnpm next upgrade

2. Codemod for earlier versions

npx @next/codemod@canary upgrade latest

3. Async params

const { slug } = await props.params

4. Parallel route default

// app/@modal/default.tsx
export default function Default() { return null }

5. Middleware rename

# middleware.ts → proxy.ts
# middleware → proxy

6. Lint migration

npx eslint .

7. Runtime config

process.env.NEXT_PUBLIC_API_URL

8. Image defaults revert

images: { minimumCacheTTL: 60 }

9. Canary install

pnpm add next@canary

10. Build check

pnpm build

Visual

The upgrade decision tree

┌─────────────────────────────────────────────────────────────┐
│  Which channel?                                              │
│       │                                                      │
│       ├── Stable ──► next@latest                             │
│       │     └── planned migration, documented changes       │
│       │                                                      │
│       └── Canary ──► next@canary                             │
│             └── unreleased features, unstable                │
└─────────────────────────────────────────────────────────────┘

The codemod coverage

┌─────────────────────────────────────────────────────────────┐
│  CODEMOD HANDLES                MANUAL                       │
│                                                              │
│  async params                   middleware → proxy           │
│  async searchParams             parallel default.js          │
│  async cookies()                next lint removal            │
│  async headers()                runtime config removal       │
│  async draftMode()              AMP removal                  │
└─────────────────────────────────────────────────────────────┘

The upgrade flow

┌─────────────────────────────────────────────────────────────┐
│  git commit (checkpoint)                                     │
│       │                                                      │
│       ▼                                                      │
│  next upgrade                                                │
│       │                                                      │
│       ▼                                                      │
│  manual migrations                                           │
│       │                                                      │
│       ▼                                                      │
│  pnpm build                                                  │
│       │                                                      │
│       ├── success ──► deploy                                 │
│       │                                                      │
│       └── failure ──► fix and rebuild                        │
└─────────────────────────────────────────────────────────────┘

The canary risk

┌─────────────────────────────────────────────────────────────┐
│  Canary: every commit, not every feature is stable.          │
│  Stable: tested, documented, supported.                      │
│                                                              │
│  Production should track stable unless there is a reason.    │
└─────────────────────────────────────────────────────────────┘

Summary

ItemValue
Stable upgrade commandpnpm next upgrade (16.1.0+)
Earlier upgrade commandnpx @next/codemod@canary upgrade latest
Canary installpnpm add next@canary
Node.js requirement20.9+
React requirement19.2+
Codemod coverageAsync request APIs
Manual migrationsMiddleware rename, parallel default, lint, runtime config
Build verificationpnpm build

Key takeaways:

  • The stable channel is the default for production. next@latest resolves to the stable release, and the version guides document the breaking changes. The canary channel is for unreleased features and carries the risk of undocumented changes .
  • The upgrade command automates the version bump and the codemods. pnpm next upgrade is available from 16.1.0. Earlier versions use the @next/codemod package. The codemod handles the async request APIs but not the structural migrations .
  • The middleware rename is manual and has deployment implications. middleware.ts becomes proxy.ts, and the exported function becomes proxy. The Edge runtime is not supported, and OpenNext does not recognize the new filename. A project on Cloudflare must keep middleware.ts until the support is added .
  • Parallel routes require a default.js file. Without it, the build fails. The file renders null when the slot has no active match .
  • next lint is removed. The migration is to use ESLint directly with the eslint-config-next package. The next lint command no longer exists .
  • The image defaults changed. The cache TTL, the imageSizes array, and the qualities array are different from the previous version. A project that relied on the old values can revert them in next.config.ts .

Remember: Upgrading Next.js is a planned migration, not a package bump. Start with the version check, run the upgrade command, and then work through the manual changes. The codemod handles the async APIs; the middleware rename, the parallel default file, and the lint migration are manual. Verify with a build before deploying. And keep the canary channel for the cases that need an unreleased feature, not for the projects that simply want the latest release.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!