Next.js Documentation Audit
The docs are vast, well-organized, and ship with thoughtful agent affordances (llms.txt, llms-full.txt, .md URL aliases, versioned snapshots). But the v16 release introduced a wave of breaking changes that have only partially propagated through the reference and guide pages — leaving copy-paste contradictions, dead-slug anchors, half-shipped migrations, and a Pages Router that quietly links into App Router territory.
1. Async Request APIs: v16 upgrade guide says sync is fully removed, but reference pages don't reflect it (critical)
Location: /docs/app/guides/upgrading/version-16 vs /docs/app/api-reference/functions/cookies, /docs/app/api-reference/functions/headers, /docs/app/api-reference/file-conventions/page
Problem: The v16 upgrade guide is unambiguous: "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" — applied to cookies, headers, draftMode, params, and searchParams. The individual API reference pages for cookies and headers simply describe them as "an async function" without flagging that synchronous access was permitted in v15 and is now gone in v16. There is no version-removal callout, no "Breaking change in 16" banner, no migration pointer back to the upgrade guide on either reference page.
Consequence: A developer migrating from v15 to v16 lands on the cookies reference, sees nothing alarming, and concludes their existing sync usage is still fine. They ship code that breaks at runtime (or at build time) without ever seeing the upgrade-guide notice. Agents indexing only the API references — which is exactly how agents discover function signatures — emit code that was valid in v15 and is now removed.
The fix: Add a ## Version History table or a prominent "Breaking change in v16" callout to /cookies, /headers, /draft-mode, and to the params / searchParams sections of /docs/app/api-reference/file-conventions/page. Each callout should link to the v16 upgrade guide section and quote the "synchronous access is fully removed" line.
2. revalidateTag arity contradicts itself across two adjacent v16 pages (critical)
Location: /docs/app/guides/upgrading/version-16 vs /docs/app/guides/caching-without-cache-components and /docs/app/api-reference/functions/revalidateTag
Problem: The v16 upgrade guide states plainly: "revalidateTag now requires a second argument specifying a cacheLife profile. The single-argument form is deprecated and will produce a TypeScript error." The caching guide (current v16 docs) still demonstrates and recommends the single-argument call revalidateTag('user') under its revalidateTag heading. The function's own API reference leads with the prose "revalidateTag allows you to invalidate cached data on-demand for a specific cache tag" without surfacing the new required argument in a visible signature.
Consequence: A developer (or an agent extracting the snippet) lands on the caching guide, copies revalidateTag('user'), and gets a TypeScript error on build. They have no way to know the second argument exists unless they happen to read the upgrade guide first. Agents indexing the docs record both forms as valid and emit broken code.
The fix: Update every revalidateTag(...) example in /docs/app/guides/caching-without-cache-components and anywhere else in v16 docs to use the new two-argument form. Make the API reference page lead with the full signature including the required cacheLife profile argument and an explicit "since v16" note.
3. Pages Router installation page links to App Router file convention (critical)
Location: /docs/pages/getting-started/installation
Problem: The Pages Router installation guide instructs users to "Create a public folder at the root of your project" — the linked reference is the App Router file convention, not a Pages Router page. The two routers have separate sidebars, separate reference structures, and the App Router file conventions docs include App-Router-only concepts (layout.tsx, server components context) that don't apply to a Pages Router project.
Consequence: A developer choosing Pages Router (often deliberately, for stability or legacy reasons) is silently dumped into the App Router branch of the docs the moment they click a link. The sidebar context flips. They start reading conventions that don't apply to their pages/ directory. This is the kind of cross-contamination that drives the "I followed the docs and nothing matches my project" support thread.
The fix: Either create a Pages-Router-scoped public folder reference and link to it, or qualify the link text ("see App Router public folder reference — same behavior in Pages Router") so the reader knows they're crossing a router boundary.
4. cacheHandler config entry links to the old incrementalCacheHandlerPath slug, with a cacheHandler vs cacheHandlers naming split (significant)
Location: /docs/app/api-reference/config/next-config-js and /docs/app/api-reference/directives/use-cache
Problem: Two related issues at the same surface:
- The top-level config index lists the modern key
cacheHandler, but its anchor link is/docs/app/api-reference/config/next-config-js/incrementalCacheHandlerPath— the legacy slug. The same legacy slug also appears inside theuse cachedirective page, where "Control cache size withcacheMaxMemorySize" links to/docs/app/api-reference/config/next-config-js/incrementalCacheHandlerPathrather than acacheMaxMemorySizepage. - The
use cachedirective page referencescacheHandlers(plural): "You can customize the cache storage by configuringcacheHandlersin yournext.config.jsfile." But the config index listscacheHandler(singular). It is unclear whether these are two distinct config keys or a doc inconsistency.
Consequence: Anchor links land users on a page named after a deprecated key. Worse, a developer trying to configure cache storage doesn't know whether to write cacheHandler: ... or cacheHandlers: { ... }. Agents following the links ingest content keyed to the old name and may emit either form. Either way, copy-paste fails.
The fix: Move the content to a cacheHandler slug (with a redirect from the legacy slug), and reconcile cacheHandler vs cacheHandlers — if they are distinct keys, give each its own page and cross-link them; if one is wrong, fix the references. Give cacheMaxMemorySize its own page so the use cache cross-reference resolves correctly.
5. next.config.js index lists legacy/experimental options alongside current ones with no status indicator (significant)
Location: /docs/app/api-reference/config/next-config-js
Problem: The scraped index mixes exportPathMap ("Customize the pages that will be exported as HTML files when using next export"), urlImports, and useLightningcss ("experimental support for Lightning CSS") alongside current options like turbopack and taint without any deprecated/legacy/experimental flag in the index. next export is the legacy command, but exportPathMap is presented identically to modern entries. The v16 upgrade guide elsewhere lists removals (amp, serverRuntimeConfig, next lint), but the config index doesn't carry that signal.
Consequence: A developer scanning the config surface area can't tell at a glance which options are part of the modern recommended path vs. lingering for compatibility vs. experimental. Agents auto-generating config files will happily emit exportPathMap because it's in the index without a warning.
The fix: Add a Status column to the config index (Stable / Experimental / Legacy / Deprecated), matching what the next/image props table already does. Mark exportPathMap, urlImports, useLightningcss, and similar entries explicitly.
6. middleware → proxy migration leaves edge-runtime users with no concrete plan (significant)
Location: /docs/app/guides/upgrading/version-16 and /docs/app/api-reference/file-conventions/proxy
Problem: The upgrade guide states: "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." No version, no timeline, no link to a tracking issue.
Consequence: Anyone running Edge middleware in production has to choose: stay on the deprecated middleware filename indefinitely (because proxy won't run on edge), or migrate to proxy and lose edge execution. The docs acknowledge the gap but punt to an unspecified future minor release. Platform teams making upgrade plans have nothing to plan against.
The fix: Either ship the edge-runtime story alongside the rename (no half-shipped migrations in a major), or link to a public tracking issue with a target version. Add an explicit "if you depend on edge middleware, do not upgrade yet" callout to the upgrade guide.
7. unstable_rootParams removed in v16 with the same "we'll ship something later" gap (significant)
Location: /docs/app/guides/upgrading/version-16
Problem: Under Removals, the v16 upgrade guide states: "The unstable_rootParams function has been removed. We are working on an alternative API that we will ship in an upcoming minor release." No timeline. No link to a tracking issue. No interim workaround. This mirrors the edge-runtime gap in finding 6 — a feature was pulled before its replacement landed.
Consequence: Anyone who built on unstable_rootParams (and the unstable_ prefix is explicitly the framework's invitation to do so under known instability) is now stuck: their code no longer compiles, and the docs offer no migration target. They cannot plan around a "coming soon" with no version anchor.
The fix: Ship the replacement before removing the old API in a major, or — if it must be removed first — provide a documented interim pattern (e.g., a layout-level workaround) and a link to a tracked issue with a target minor version.
8. PPR migration guide tells production users to "stay in the current Next.js 15 canary" (significant)
Location: /docs/app/guides/upgrading/version-16
Problem: Under Partial Prerendering, the v16 upgrade guide says: "PPR 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. See Migrating to Cache Components for migration patterns."
Consequence: This is unusual upgrade guidance: production users who adopted PPR are told to pin to a 15.x canary release — not a stable LTS — until they migrate to Cache Components. Canaries are not covered by the Active LTS or Maintenance LTS support policy listed in llms.txt, so anyone following this advice is now running an unsupported build in production with no security-patch guarantee. The doc does not flag the support-policy implication, name a specific canary version, or set an end date for this state.
The fix: Either commit to back-porting security fixes to the specific canary line PPR users are pinned on (and name that line in the doc), or provide a stable 15.x → 16 migration path that doesn't require sitting on a canary. Explicitly call out that the support-policy table in llms.txt does not cover canary releases.
9. generateMetadata page still documents three options it flags as deprecated since v14 (significant)
Location: /docs/app/api-reference/functions/generate-metadata
Problem: The generateMetadata reference includes themeColor, colorScheme, and viewport as documented metadata options. Each has a "Deprecated" note pointing to the viewport configuration — "deprecated as of Next.js 14" — yet the deprecation has now persisted through v15 and v16 with the fields still documented prominently as part of the metadata surface, with no removal timeline. The version history table records the v13.2.0 deprecation but lists no later entry indicating when they will be removed.
Consequence: Two-version-old deprecation notices that never resolve teach developers (and agents) to ignore deprecation banners. New code continues to be written against themeColor and colorScheme because they're documented as supported options. When the eventual removal lands, the migration will be larger than it needed to be.
The fix: Either commit to a removal version and add it to each deprecation banner ("removed in v17"), or move the deprecated fields into a clearly separated "Legacy options" section so they aren't presented as peer to current fields.
10. "Verified adapters" advertised as differentiated, but the test results aren't actually published (significant)
Location: /docs/app/getting-started/deploying
Problem: Verified adapters are sold as "open source, run the full Next.js compatibility test suite ... The Next.js team coordinates testing with these platforms before major releases." Then: "Publicly visible test results for each adapter are coming soon." Today, May 15 2026 — more than six months after the v16 release in October 2025 — the docs still say "coming soon."
Consequence: The whole point of the "verified" tier is the verification. Without published results, a developer can't tell whether Vercel and Bun are passing 100% of the suite or 60%. The "Other Platforms" tier (Cloudflare, Netlify, AWS Amplify, etc.) is then defined relative to a verification standard whose outputs aren't visible. The distinction is marketing without receipts.
The fix: Either publish the test results (even as a static dashboard with a "last run" timestamp), or remove the "coming soon" claim and describe what verification actually means today.
11. next/link version history has a v15.3.0 → v13.0.0 gap that hides the v15 default-prefetch behavior change (minor)
Location: /docs/app/api-reference/components/link
Problem: The next/link Version history records v16.2.0 (transitionTypes), v15.4.0 ("Add auto as an alias to the default prefetch behavior"), and v15.3.0 (onNavigate), then jumps straight to v13.0.0. The prose elsewhere on the page describes the current prefetch="auto" | null semantics where dynamic routes prefetch "the partial route down to the nearest segment with a loading.js boundary" — behavior that changed during the v15 release line — but the history table has no entry for when that default semantics change landed (it appears only as the v15.4 "alias" entry, which is misleading because the semantics themselves shifted, not just the naming).
Consequence: A team debugging a prefetch behavior difference between v14 and v15 reads the version history, sees only a renaming entry, and concludes the underlying behavior didn't change. They then spend hours chasing what is actually documented behavior.
The fix: Add a v15 entry that explicitly records the default prefetch semantics change (not just the auto aliasing) and link to the v15 upgrade guide section that explains the migration.
12. searchParams description links to an MDN URL-parameters page but resolves to a plain object (minor)
Location: /docs/app/api-reference/file-conventions/page
Problem: The searchParams documentation reads: "A promise that resolves to an object containing the search parameters of the current URL." The MDN link describes URL search parameters generically, which is the conceptual home of URLSearchParams. But the value the Next.js promise resolves to is a plain object, not a URLSearchParams instance. The page does not show the actual shape inline, so a reader leaning on the linked reference will form the wrong expectation.
Consequence: A developer who follows the MDN link expects to call .get(), .getAll(), .entries() on the resolved value, then gets confused when those methods aren't present. Agents pattern-matching on the MDN cue may emit URLSearchParams code.
The fix: Either drop the MDN link or replace it with an inline type signature (and a one-line example object) on the same page, so the resolved shape is documented at the point of reference rather than implied by an external link.
What they do well
- Agent-first surface:
llms.txt,llms-full.txt, versioned/docs/{version}/llms.txt, and the.mdURL extension are genuinely useful for AI agents. This is ahead of most peer frameworks. - Two-router clarity at the top: The landing page explains the App Router vs Pages Router split early and surfaces a router dropdown — when intra-page links are correct (see finding 3).
- Version history tables on most reference pages: Concrete "in this version, this changed" records — when complete (see findings 1 and 11) — are exactly what migration planning needs.
Top 3 recommendations
- Sweep every v16 reference page that documents an async-only API (
cookies,headers,draftMode,params,searchParams) and add an unmissable "Breaking change in v16: synchronous access removed" callout with a link to the upgrade guide. Without it, the upgrade guide is the only place that holds the truth, and most developers (and all agents) read references first. - Resolve the
cacheHandler/cacheHandlers/incrementalCacheHandlerPathslug-and-key mess in one pass. The config index is the natural starting point for both humans and agents, so naming inconsistencies and dead anchors there compound everywhere downstream. - Either ship or retract the half-migrated v16 stories — edge-runtime for
proxy, the replacement forunstable_rootParams, and the PPR canary pin — before the next major. A "we'll follow up later" promise written into a major release is the worst of both worlds for production teams planning upgrades.