If you learned the App Router before 2025, you learned a diagram with four boxes: request memoization, the Data Cache, the Full Route Cache and the client Router Cache, sitting behind a fetch that cached everything it could unless you passed cache: 'no-store'. That model went in two steps. Next.js 15 10,514 flipped the fetch default, so requests are no longer cached unless you ask. Next.js 16 made the whole render dynamic by default and introduced Cache Components as the single way back in. The segment exports that used to steer the old caches — dynamic, dynamicParams, revalidate and fetchCache — are removed when Cache Components is enabled, and experimental.ppr and experimental.dynamicIO were deleted outright.
| Concern | Next.js 13-14 | Next.js 16.3 with Cache Components |
|---|---|---|
| fetch in a Server Component | cached by default | never cached by default |
| Cross-request data cache | Data Cache, implicit | use cache on a function |
| Cached page output | Full Route Cache, implicit | use cache on a page or layout |
| Lifetime | revalidate export, next.revalidate | cacheLife profile |
| Invalidation | revalidateTag, revalidatePath | updateTag, revalidateTag, refresh |
| Mixing static and dynamic | experimental.ppr | on by default |
One piece of the old diagram survives unchanged: React 7,897 still deduplicates identical fetch calls inside a single render pass, so two components asking for the same URL make one request. That is request memoization, not a cache across requests, and Parallel Requests covers it.
Migration is mostly subtraction. Delete export const revalidate, drop cache: 'force-cache' and next: { revalidate } from your fetch calls, then add "use cache" and a cacheLife to the functions whose results you actually want shared. If you are not ready, the previous model still works: leave cacheComponents off and keep unstable_cache and the segment exports. The two are alternatives, not layers.