How do I stop my AI coding tool suggesting outdated Next.js patterns?

Ivo proudly holding open a huge, worn paper road map beside a lamppost on a gravel patch while Mira leans in from the side to look at it, a hand over her mouth to hide a laugh.

TL;DR

AI coding tools still suggest Pages Router data fetching, synchronous params or a middleware.ts file in Next.js projects that have moved on. Read the installed version and router from the lock file and folders, point the agent at the documentation bundled with that version, and back the instructions with a build, a lint pass and a search for retired APIs. Update the rules and examples in the same pull request as each upgrade, so the remaining stale suggestions fail a check before anyone reviews them.

The framework now warns agents about their own memory

Since Next.js 16.3, running next dev in a project where an AI coding agent is detected writes a managed block into AGENTS.md. Its heading reads "This is NOT the Next.js you know", and it tells the agent to read the guide bundled in node_modules/next/dist/docs/ before writing code and to heed deprecation notices (Next.js AI Coding Agents guide, documentation for 16.3.8, updated 7 September 2026).

Developers had described the problem long before. In July 2025 a proposal on the Next.js discussion board asked for a standard set of instructions for AI tools, because they often produce Pages Router code where the App Router is wanted, misuse useEffect for data fetching in Server Components, or ignore the Metadata API. In January 2025 a Cursor user asked on the Cursor forum how to keep generated code in line with the module versions actually installed. Their example was Python; a staff reply said there was no easy way to cover every dependency.

"Outdated" is relative to your project, which is why a generic instruction such as "use modern Next.js" helps little. The App Router has been stable since Next.js 13.4 in May 2023, yet the Pages Router is still documented and supported, and both can run in one application. Other changes are hard breaks. Next.js 16, released in October 2025, removed synchronous access to cookies, headers, params and searchParams, deprecated the middleware file convention in favour of proxy, and removed the next lint command (version 16 upgrade guide). Code that was right on 14 can fail on 16, and code written for 16 can be wrong on 14.

Find out which Next.js this project actually runs

First establish what correct means here, and make the agent do the same. Four facts settle most disagreements:

What to read before the first edit

FactWhere to read itWhat it decides
Installed next versionThe lock file, or npm ls next / pnpm why nextWhich documentation, APIs and codemods apply
Router for each routeapp/ and pages/, at the root or under src/Server Components and next/navigation, or data-fetching functions and next/router
Opt-in rendering featuresnext.config.*, for example cacheComponentsWhether "use cache" and the Cache Components rules apply
React and TypeScript versionsThe lock fileWhich hooks and types generated code may use

Take versions from the lock file, not the range in package.json: ^15.0.0 says what is allowed, the lock file what is installed. Commit it so every machine and agent session gets the same answer. In a monorepo, run the check from the application's own directory; the managed AGENTS.md block itself warns that the next package may not be visible from the repository root.

Then put those facts in front of the agent and ask it to repeat them. A first line in the task such as "State the installed Next.js version and the router for the files you will touch before proposing changes" turns a silent assumption into a visible one. If the agent answers "Next.js 14, Pages Router" in an App Router project on 16, you have caught the problem before any code exists.

Illustration seen from above of Ivo sitting cross-legged in socks, holding an old desk telephone upside down and studying the small blank plate on its base, the handset resting on his knee, an open cardboard box of tangled cables beside him and his shoes set to one side.
Before calling a suggestion outdated, check what this project actually has installed.

Give the agent the documentation for the installed version

A model's built-in knowledge stops at some point, and nothing in it says which version you have. Next.js now ships the documentation half of the answer inside the package, but how you use it depends on the version:

  1. Next.js 16.3 or later. next dev writes and refreshes the managed block in AGENTS.md, plus a CLAUDE.md containing @AGENTS.md. Your own instructions outside the markers are kept. Commit both files; agentRules: false in the Next.js config turns this off.
  2. Next.js 16.2. The documentation is bundled in node_modules/next/dist/docs/, but you write the AGENTS.md instruction yourself.
  3. Next.js 16.1 and earlier. npx @next/codemod@canary agents-md downloads a version-matched copy into .next-docs/ and indexes it in AGENTS.md.

The CLAUDE.md import matters for Claude Code: its memory documentation says it reads AGENTS.md on its own only when no CLAUDE.md exists, so a project with both files needs the import. The article on keeping one set of project instructions across AI coding tools covers how other tools read the same file.

Two other sources can quietly undo this. One is the website: nextjs.org serves its pages as Markdown with an llms.txt index, which on 1 October 2026 described itself as the documentation for 16.3.8. That suits an app on the latest release and misleads one on 15. The other is your own repository. An agent imitates nearby code, so a half-migrated pages/ route or an old README snippet teaches the old pattern as effectively as stale training data. Choose one current example per pattern (a page that reads data on the server, a Client Component, a Server Action, a route handler), name them in AGENTS.md, and mark code that must stay legacy as legacy. If old patterns make another framework tempting, the article on switching a working Vue project to React for AI tools weighs it.

Vercel reports that in its own agent evaluations, always-available bundled documentation beat skills the agent had to decide to load (Next.js 16.2 AI post); that is the vendor measuring its own setup, not a guarantee for yours.

Catch the wrong boundary and the retired API before review

Instructions lower the rate of stale suggestions; checks catch the ones that get through. The server-client boundary deserves the most care, because a mistake there pulls code meant for the server toward the browser. The Server and Client Components guide recommends marking server-only modules so that importing one into a Client Component becomes a build-time error. An illustrative example, written for this article and not run:

// lib/orders.ts: reads the database, must never reach the browser
import 'server-only'

export async function getOrders(customerId: string) {
  // database query
}

// app/orders/order-filter.tsx
'use client'
import { getOrders } from '@/lib/orders' // fails the build: wrong side of the boundary

Retired APIs need a cruder check: a text search, which works whichever tool wrote the code. A bare grep is no CI gate, since it exits 0 when it finds the forbidden pattern. This wrapper inverts that, searches only folders that exist, and reports operational errors separately:

#!/usr/bin/env bash
# scripts/check-retired-next.sh
# exit 0: clean; exit 1: retired pattern found; exit 2: the search itself failed
set -u
pattern="getServerSideProps|getStaticProps|getInitialProps|from ['\"]next/router['\"]"
found=0
dirs=()
for d in app src/app; do [ -d "$d" ] && dirs+=("$d"); done
if [ ${#dirs[@]} -gt 0 ]; then
  grep -rnE --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' \
    "$pattern" "${dirs[@]}"
  rc=$?
  if [ "$rc" -eq 0 ]; then found=1
  elif [ "$rc" -ne 1 ]; then echo "search failed (grep exit $rc)" >&2; exit 2; fi
fi
for f in middleware.ts middleware.js src/middleware.ts src/middleware.js; do
  if [ -e "$f" ]; then echo "$f: Next.js 16 uses proxy.ts"; found=1; fi
done
exit "$found"

We ran it against four throwaway fixtures with BSD grep on macOS and GNU grep on Linux: a clean app/ tree exited 0, a next/router import exited 1, a root middleware.ts exited 1, and an unreadable source file exited 2. Text matching is blunt: it also flags a comment mentioning getStaticProps, and the middleware check belongs only in projects already on 16. Where a lint rule can express the ban, prefer it.

  • next build on every pull request. Its type check flags synchronous use of params, which the generated types describe as a Promise, and it fails on prerendering errors.
  • ESLint or Biome run directly. Since Next.js 16, next build no longer lints and next lint is gone.
  • The wrapper above in CI, extended whenever an upgrade retires something.
  • The official codemods for documented migrations, with their diff reviewed.
  • Runtime errors during development. On Next.js 16 and later, the Next.js MCP server (next-devtools-mcp) lets an agent read build, runtime and type errors from the running dev server.

Change the rules in the same pull request as the upgrade

After an upgrade, many stale suggestions come from your own instructions and examples. The version 16 upgrade guide now opens with a prompt for an agent, and it asks you to confirm afterwards that AGENTS.md points at documentation for the installed version and to remove .next-docs/ once nothing references it. In April 2026 a developer reported in the Next.js discussions that AGENTS.md was not refreshed after npm i next@latest; a maintainer replied that the next upgrade command would take care of it. Let the upgrade pull request carry everything that defines "current":

  1. The version change and the new lock file.
  2. Codemod output, reviewed like any other diff.
  3. A refreshed managed block, with .next-docs/ removed if it was used.
  4. Your own rule lines rewritten for the new version, for example "params is a Promise" or "proxy.ts, not middleware.ts", and the old lines deleted.
  5. The canonical examples migrated first, so the agent copies the new pattern.
  6. New search patterns for anything the release removed.

Paqad helps with noticing, not with knowing Next.js. Its stack detection reads manifests and lock files, records the pinned next version and its major-version band, and marks App Router and Pages Router traits from the app/ and pages/ folders. After an upgrade, the drift report lists the band change and names the stack rules and architecture documents to review; someone still reviews the updates. During planning, a reuse claim naming a framework symbol that is missing or deprecated in the installed package is refused. It does not make any model better at Next.js: version-specific rules are yours to write, and a Pages Router function inside app/ is a convention mistake rather than a deprecated symbol, so the build and the wrapper still matter. Paqad's drift-detection documentation shows what a major version change flags and where the report is kept.

Illustration of Ivo, one hand on his chest and a mock-solemn face, lowering a folded paper map into a cardboard archive box on a two-drawer filing cabinet while Mira lifts the lid and grins at him.
Retire the old rules and examples in the same change that retires the old version.

Frequently Asked Questions

You cannot remove what a model learned, so make the mismatch visible and cheap. Ask for the version and router at the start of each task: a wrong answer in the first line costs seconds, while the same assumption found in review costs a rewrite.

Answer per route, not per project. During an incremental migration the same application can serve some routes from app/ and others from pages/, so the right pattern depends on where the file lives. Record which router new routes must use, so the agent does not extend the legacy one.

Give the agent one documentation source and make it the one tied to the installed package. Pasting website pages or a blog post into the chat adds a second source with a different version, and the agent has no way to know which one wins.

Put them in CI rather than only in the agent's instructions. An instruction is read by one tool in one session; a failing build or search stops the change whatever produced it, including code a person wrote by hand.

Write the version into the rule file itself, for example a first line naming the major version it describes. When the lock file and that line disagree, the rules are stale, and a reviewer can spot it without reading every rule.