Next.js Upgrade Documentation: Version 12 to 16

3rd Aug, 2026 | Shubham G.

  • Software Development
Next.js Upgrade Documentation

Overview

This document captures the complete upgrade of a Frontend App from Next.js 12 to Next.js 16, including all breaking changes encountered, migration strategies employed, and lessons learned during the process.

Architecture Decision Record (ADR)

Status: Accepted and Completed

Context

We needed to upgrade our Frontend App from Next.js 12 to Next.js 16. This upgrade involved:

  • Migration from Pages Router to App Router
  • React 18 to React 19 upgrade
  • Node.js 18 to Node.js 20.9.0+ requirement
  • Multiple breaking changes in core packages (MUI, routing, middleware)
  • multiple package dependency updates

Two approaches were considered:

1. Version-by-version upgrade

Upgrade Next.js incrementally (12→13→14→15→16), fixing breaking changes at each step.

2. Module-by-module upgrade

Jump directly to target version, then fix broken modules and breaking changes one by one

Decision

We chose the module-by-module approach - upgrading directly to Next.js 16 and fixing each broken module systematically.

Why we rejected version-by-version:

  • Time constraints made incremental upgrades impractical
  • Each version step would require full regression testing
  • Many breaking changes compound across versions anyway
  • Would require maintaining intermediate states

Why module-by-module worked:

  • POC-first validation: Fixed one page (login) completely before proceeding, proving the approach viable.
  • Skeleton structure creation: Used Cursor Plan Mode to generate App Router structure, accepting a "broken but structured" starting point
  • Import aliases preserved stability: Configured @src/, @config/ aliases in tsconfig.json and next.config.js so directory restructuring didn't break imports
  • Grouped similar fixes: Batched TypeScript errors and package migrations by pattern (e.g., all toaster.notify → ShowErrorToaster replacements done together)

Consequences

Positive:

  • Faster overall migration (no intermediate version)
  • Clear progress tracking (module-by-module checklist)
  • Easier to parallelize work across team members by distributing specific modules to my peers.
  • Single target state to test against, No "works in v14 but breaks in v15" surprises.

Negative:

  • The initial state was completely broken.
  • Required comprehensive planning upfront.
  • Harder to isolate root cause when multiple things broke simultaneously

Mitigations applied:

  • Used overrides in package.json for peer dependency conflicts.
  • Leveraged Cursor for bulk pattern replacements.

3. Pre-Migration Planning

1. Create Comprehensive Upgrade Plan

  • Created a comprehensive upgrade plan in Cursor using Plan Mode
  • Provided full repository context and included key files:
    • package.json
    • next.config.js
    • _app.tsx (Pages Router)
    • _document.tsx
  • Used Bombay Softwares Next.js boilerplate as reference for App Router structure

2. Configure package.json for Compatibility

Added the overrides section in package.json to handle React 19, Next.js 16, and Node 22 compatibility, This prevents errors during npm install.

{
  "engines": {
    "npm": ">=6.0.0",
    "node": ">=20.9.0"
  },
  "overrides": {
    "react-html-parser": {
      "react": "^19.0.0",
      "react-dom": "^19.0.0"
    },
    "@recogito/annotorious-openseadragon": {
      "openseadragon": "^5.0.1"
    }
  }
}

3. Execute Plan - Create Skeleton Structure

  • Executed the plan in Cursor
  • This allowed creation of a skeleton structure of the newer version of Next.js
  • Expected outcome: A broken application where issues can be fixed one by one

4. POC

  • For proof-of-concept, completely fixed only 1 page (login page) of the newer version
  • This validated that the upgrade was possible before proceeding with full migration
  • Once login page worked end-to-end, we proceeded with remaining pages

4. Directory Structure Migration

1. Import Aliases Configuration

  • Added aliases for imports which resolved import issues when directory structure changed
  • This approach didn't break any imports during the restructuring

tsconfig.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@src/*": ["src/*"],
      "@public/*": ["public/*"],
      "@interfaces/*": ["src/interfaces/*"],
      "@config": ["src/config"],
      "@config/*": ["src/config/*"]
    }
  }
}

next.config.js:

webpack: (config) => {
    config.resolve.alias = {
        ...config.resolve.alias,
        '@src': path.resolve(__dirname, 'src'),
        '@public': path.resolve(__dirname, 'public'),
        '@interfaces': path.resolve(__dirname, 'interfaces'),
        '@config': path.resolve(__dirname, 'config')
    };
    return config;
}

2. Reference Boilerplate

  • Used Bombay Softwares' Next.js boilerplate for App Router project structure.
  • We created a plan in cursor to migrate the page router to app router structure.
  • Updated directory structure to match the boilerplate conventions

3. Stylesheet Migration

  • Updated stylesheets to work with newer version of Next.js
  • Used the official SASS migration tool:
npx sass-migrator division **/*.scss

Reference: https://sass-lang.com/documentation/cli/migrator/

5. Next.js Breaking Changes Resolution

Next.js 12 → 13

| Change | Details | | --- | --- | | next/link | Before: 'textAfter: text No need for nested tag | | Imports | Before: import { useRouter } from next/router'After: import { useRouter, usePathname, useSearchParams } from 'next/navigation` |

Next.js 13 → 14

  • Node.js minimum: 18.17
  • No other breaking change in Frontend

Next.js 14 → 15

  • React bump to 19: Next.js 15 requires React 19
  • No other breaking change in Frontend

Next.js 15 → 16

| Change | Details | | --- | --- | | Node.js minimum | 20.9.0 - Node 18 is no longer supported | | TypeScript minimum | 5.1+ | | Default bundler | Turbopack becomes the default bundler | | Middleware API | The old middleware.ts API is deprecated/replaced by proxy.ts as the primary network boundary file |

6. Layouts and Route Groups

Created route groups as per Next.js Route Groups documentation:

Layouts:

  • Main Layout (src/app/layout.tsx) - Under app directory
    • Providers are added here
    • Script tags are added here
  • Auth Layout (src/app/(auth)/layout.tsx) - Under auth group
    • Authentication wrapper
  • Navbar Layout (src/app/(auth)/(navbar)/layout.tsx) - Under navbar group
    • Navbar component is added here

Route Groups:

  • (auth) group - All routes that need authentication are grouped here
  • (navbar) group - All routes that need a Navbar are grouped here
    • Navbar group is nested under auth group

22.png App Router Structure

7. Pages Redesign

Error Page (500)

  • Updated Error page to a newer UI
  • Attached Figma Screenshots in prompt to Cursor
  • Attached required assets to create the page

File: src/app/error.tsx

  • Expandable error details
  • Retry button
  • Help center links

Not Found Page (404)

  • Created a new 404 not found page
  • Used Figma Screenshots in prompt to Cursor
  • Attached required assets to create the page

File: src/app/not-found.tsx

  • Branded design with gene icon
  • Helpful navigation links

Appointment Booking Page Separation

Problem: Appointment booking had the same route but different UI for:

  • Logged in users
  • Logged out users

Solution: Separated the appointment booking page into 2 different pages:

  • Logged in users - Came under (auth) group: src/app/(auth)/book-appointment/
  • Logged out users - Came out of (auth) group: src/app/book-appointment/

8. Package Compatibility Updates

1. Package JSON Review

Once all breaking changes were resolved, started picking up packages one by one to ensure compatibility with:

  • Current Next.js version
  • Current Node.js version

2. MUI Migration (Major Change)

Problem: Two different versions of MUI were installed

Task: Remove older version and upgrade newer version to latest MUI

Migration Strategy:

Breaking Changes:

| Change | Before | After | | --- | --- | --- | | @mui/styles | Supported | Removed in v7 - not supported in v6+ and fully removed in v7 | | Styling approach | makeStyles, withStyles | Migrated to styled from @mui/material/styles |

Current MUI Packages:

{
  "@emotion/react": "^11.14.0",
  "@emotion/styled": "^11.14.1",
  "@mui/icons-material": "^7.3.5",
  "@mui/material": "^7.3.5",
  "@mui/x-tree-view": "^8.19.0"
}

3. Toast Notifications Migration

Problem: toasted-notes package stopped working with newer Next.js version

Solution:

  • Removed all usages of the library
  • We were already using custom functions to invoke toaster messages. We migrated any toaster code which was not using these functions.
  • This approach allowed changes to be made only in the custom function
  • Used react-hot-toast package as replacement

Before (toasted-notes):

toaster.notify(({ onClose }) => (
  <ErrorToaster onClose={onClose} message="Message string" />
), {
  duration: 3000,
  position: "top-right",
});

After (react-hot-toast with custom wrapper):

ShowErrorToaster({
  message: "Message string",
  duration: 3000,
  position: "top-right",
});

Implementation File: src/components/ui/ErrorToaster.tsx

4. Other Package Updates

  • Used similar approach for upgrading all other packages
  • Checked compatibility one by one
  • Used overrides in package.json for peer dependency conflicts

9. TypeScript Issues Resolution

1. Strategy for Bulk TypeScript Fixes

We used Cursor to create a new file that listed all type issues in the code and Group similar type issues together Where we have mentioned file paths and line number with the issues.

Provided the grouped issues list as context to Cursor to fix in all files with the same issue and included expected solution when sure.

This approach allowed fixing multiple files with the same pattern simultaneously.

2. TypeScript Configuration

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "noImplicitAny": false
  }
}

10. Middleware to Proxy Migration

New Proxy Implementation

The old middleware.ts API is deprecated/replaced by proxy.ts as the primary network boundary file.

Business logic is based on older _app.tsx file

File: src/proxy.ts

Key features:

  • Authentication token validation via cookies
  • Permission-based routing
  • JWT payload decoding (Edge Runtime compatible)
  • Protected route handling
  • Public path allowlisting

11. Lessons Learned

1. Use Plan Mode for Large Changes

  • For any large change, use Plan Mode in Cursor
  • Break down the upgrade into manageable phases
  • Validate with POC before full migration

2. Providing Relevant Context is Important

  • Provide relevant context for accurate results
  • Include changelogs and migration guides
  • Reference official documentation
  • Attach full repository context when needed

3. Example-Driven Prompts Yield Best Results

For best results, provide an example on how to fix the issues:

Example Prompt:

Remove all usages of toaster.notify in the frontend codebase.
Replace them with ShowErrorToaster imported from @src/ui/toaster.


**Replacement Rule:**
Any usage of:
```jsx
toaster.notify(({ onClose }) => (
  <ErrorToaster onClose={onClose} message="Message string" />
), {
  duration: 3000,
  position: "top-right",
});

Must be replaced with:

```jsx
ShowErrorToaster({
  message: "Message string",
  duration: 3000,
  position: "top-right",
});

4. Systematic Package Updates

  • Update packages one by one
  • Verify compatibility with current Next.js and Node.js versions
  • Use overrides in package.json for peer dependency conflicts

5. Group Similar Issues Together

  • For TypeScript or lint errors, group similar issues
  • Fix all files with the same pattern in one go
  • Provide grouped context to AI for bulk fixes

Final Package Versions

| Package | Old | New | | --- | --- | --- | | @dnd-kit/core | ^6.1.0 | ^6.3.1 | | @dnd-kit/sortable | ^8.0.0 | ^10.0.0 | | @emotion/react | ^11.9.0 | ^11.14.0 | | @emotion/styled | ^11.8.1 | ^11.14.1 | | @mui/icons-material | ^5.6.1 | ^7.3.5 | | @mui/material | ^5.6.1 | ^7.3.5 | | @monaco-editor/react | ^4.6.0 | ^4.7.0 | | @tanstack/react-query | — | v5.90.11 | | @tinymce/tinymce-react | ^4.2.0 | ^6.3.0 | | ag-grid (community/enterprise/react) | 32.0.2 | ^33.3.2 | | axios | ^0.19.2 | ^1.13.2 | | react-bootstrap | ^1.4.3 | ^2.10.10 | | react-hot-toast | — | v2.6.0 | | react-html-parser | ^2.0.2 | ^5.2.10 | | react-markdown | ^6.0.3 | ^10.1.0 | | react-mentions | ^4.2.0 | ^4.4.10 | | react-modal | ^3.11.2 | ^3.16.3 | | react-pdf | ^5.7.2 | ^10.2.0 | | react-select | ^3.2.0 | ^5.10.2 | | react-select-async-paginate | ^0.5.3 | ^0.7.11 | | react-timezone-select | ^2.1.5 | ^3.2.8 | | react-tooltip | ^4.2.13 | ^5.30.0 | | reactflow | ^11.11.3 | ^11.11.4 | | recharts | ^2.0.8 | ^3.5.1 | | styled-components | ^5.2.3 | ^6.1.19 | | yup | ^0.27.0 | ^1.7.1 |

References

More blogs in "Software Development"

Grocery App Development
  • Software Development
  • 26th May, 2025
  • Rinkal J.

Grocery App Development Cost: A Complete Breakdown for 2025

In this blog, we’ll take a deep dive into the costs involved in developing a grocery app in 2025. From understanding the market trends in...
Keep Reading
Lawn Care App Development
  • Software Development
  • 3rd Feb, 2025
  • Aarav P.

Lawn Care App Development Guide for 2025

Looking to build a lawn care app in 2025? This guide covers market trends, key benefits, must-have features, development steps, and cost insights._ Introduction Lawn care has...
Keep Reading
mobile app development
  • Software Development
  • 7th Jul, 2025
  • Rohit M.

Top 15 Mobile App Development Trends to Watch in 2025

The mobile app development landscape is constantly evolving, with new technologies and trends shaping how apps are built and used. As we head into 2025,...
Keep Reading
Sheridan, USA Flag
Sheridan, USA
Address Icon

30 N Gould St Ste N, Sheridan, WY 82801, USA

Mumbai, India Flag
Mumbai, India
Address Icon

18th Floor, Cyberone Sector 30, Vashi, Navi Mumbai, MH

Ahmedabad, India Flag
Ahmedabad, India
Address Icon

705, Colonnade - 2, Rajpath Rangoli Road, Ahmedabad, GJ

Ras Al Khaimah, UAE Flag
Ras Al Khaimah, UAE
Address Icon

BIZ01300, Compass Building, Al Shohada Road, RAK