next 16.2.9 → 16.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/.build-commit +1 -1
  2. package/dist/bin/next +2 -2
  3. package/dist/bin/next.map +0 -0
  4. package/dist/build/index.js +3 -3
  5. package/dist/build/swc/index.js +1 -1
  6. package/dist/build/turbopack-analyze/index.js +1 -1
  7. package/dist/build/turbopack-build/impl.js +1 -1
  8. package/dist/build/webpack-config.js +3 -3
  9. package/dist/bundle-analyzer/404.html +2 -2
  10. package/dist/bundle-analyzer/__next.__PAGE__.txt +1 -1
  11. package/dist/bundle-analyzer/__next._full.txt +1 -1
  12. package/dist/bundle-analyzer/__next._head.txt +1 -1
  13. package/dist/bundle-analyzer/__next._index.txt +1 -1
  14. package/dist/bundle-analyzer/__next._tree.txt +1 -1
  15. package/dist/bundle-analyzer/_not-found/__next._full.txt +1 -1
  16. package/dist/bundle-analyzer/_not-found/__next._head.txt +1 -1
  17. package/dist/bundle-analyzer/_not-found/__next._index.txt +1 -1
  18. package/dist/bundle-analyzer/_not-found/__next._not-found.__PAGE__.txt +1 -1
  19. package/dist/bundle-analyzer/_not-found/__next._not-found.txt +1 -1
  20. package/dist/bundle-analyzer/_not-found/__next._tree.txt +1 -1
  21. package/dist/bundle-analyzer/_not-found.html +2 -2
  22. package/dist/bundle-analyzer/_not-found.txt +1 -1
  23. package/dist/bundle-analyzer/index.html +2 -2
  24. package/dist/bundle-analyzer/index.txt +1 -1
  25. package/dist/client/app-bootstrap.js +1 -1
  26. package/dist/client/index.js +1 -1
  27. package/dist/compiled/next-server/pages-api-turbo.runtime.dev.js +1 -1
  28. package/dist/compiled/next-server/pages-api-turbo.runtime.dev.js.map +1 -1
  29. package/dist/compiled/next-server/pages-api-turbo.runtime.prod.js +1 -1
  30. package/dist/compiled/next-server/pages-api-turbo.runtime.prod.js.map +1 -1
  31. package/dist/compiled/next-server/pages-api.runtime.dev.js +1 -1
  32. package/dist/compiled/next-server/pages-api.runtime.dev.js.map +1 -1
  33. package/dist/compiled/next-server/pages-turbo.runtime.dev.js +1 -1
  34. package/dist/compiled/next-server/pages-turbo.runtime.dev.js.map +1 -1
  35. package/dist/compiled/next-server/pages-turbo.runtime.prod.js +1 -1
  36. package/dist/compiled/next-server/pages-turbo.runtime.prod.js.map +1 -1
  37. package/dist/compiled/next-server/pages.runtime.dev.js +1 -1
  38. package/dist/compiled/next-server/pages.runtime.dev.js.map +1 -1
  39. package/dist/compiled/next-server/server.runtime.prod.js +1 -1
  40. package/dist/compiled/next-server/server.runtime.prod.js.map +1 -1
  41. package/dist/docs/01-app/01-getting-started/02-project-structure.md +1 -1
  42. package/dist/docs/01-app/01-getting-started/03-layouts-and-pages.md +6 -4
  43. package/dist/docs/01-app/01-getting-started/04-linking-and-navigating.md +1 -1
  44. package/dist/docs/01-app/01-getting-started/05-server-and-client-components.md +1 -1
  45. package/dist/docs/01-app/01-getting-started/07-mutating-data.md +4 -3
  46. package/dist/docs/01-app/01-getting-started/09-revalidating.md +9 -8
  47. package/dist/docs/01-app/01-getting-started/13-fonts.md +1 -1
  48. package/dist/docs/01-app/01-getting-started/14-metadata-and-og-images.md +23 -9
  49. package/dist/docs/01-app/01-getting-started/17-deploying.md +1 -1
  50. package/dist/docs/01-app/02-guides/authentication.md +5 -5
  51. package/dist/docs/01-app/02-guides/backend-for-frontend.md +20 -8
  52. package/dist/docs/01-app/02-guides/cdn-caching.md +1 -1
  53. package/dist/docs/01-app/02-guides/custom-server.md +1 -1
  54. package/dist/docs/01-app/02-guides/data-security.md +19 -12
  55. package/dist/docs/01-app/02-guides/draft-mode.md +168 -105
  56. package/dist/docs/01-app/02-guides/forms.md +1 -1
  57. package/dist/docs/01-app/02-guides/how-revalidation-works.md +2 -2
  58. package/dist/docs/01-app/02-guides/mdx.md +1 -0
  59. package/dist/docs/01-app/02-guides/migrating-to-cache-components.md +533 -3
  60. package/dist/docs/01-app/02-guides/multi-zones.md +1 -1
  61. package/dist/docs/01-app/02-guides/package-bundling.md +1 -1
  62. package/dist/docs/01-app/02-guides/prefetching.md +5 -5
  63. package/dist/docs/01-app/02-guides/preventing-flash-before-hydration.md +3 -3
  64. package/dist/docs/01-app/02-guides/public-static-pages.md +1 -1
  65. package/dist/docs/01-app/02-guides/scripts.md +1 -1
  66. package/dist/docs/01-app/02-guides/server-actions.md +180 -0
  67. package/dist/docs/01-app/02-guides/streaming.md +0 -1
  68. package/dist/docs/01-app/02-guides/third-party-libraries.md +1 -0
  69. package/dist/docs/01-app/02-guides/upgrading/version-15.md +1 -1
  70. package/dist/docs/01-app/03-api-reference/01-directives/use-server.md +2 -0
  71. package/dist/docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.md +4 -2
  72. package/dist/docs/01-app/03-api-reference/03-file-conventions/instrumentation.md +18 -4
  73. package/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/rewrites.md +8 -6
  74. package/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/serverActions.md +2 -0
  75. package/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/transpilePackages.md +15 -3
  76. package/dist/docs/01-app/03-api-reference/05-config/02-typescript.md +1 -1
  77. package/dist/docs/01-app/04-glossary.md +1 -1
  78. package/dist/docs/02-pages/02-guides/upgrading/version-12.md +1 -1
  79. package/dist/docs/04-community/01-contribution-guide.md +2 -2
  80. package/dist/docs/index.md +0 -2
  81. package/dist/esm/build/index.js +3 -3
  82. package/dist/esm/build/swc/index.js +1 -1
  83. package/dist/esm/build/turbopack-analyze/index.js +1 -1
  84. package/dist/esm/build/turbopack-build/impl.js +1 -1
  85. package/dist/esm/build/webpack-config.js +3 -3
  86. package/dist/esm/client/app-bootstrap.js +1 -1
  87. package/dist/esm/client/index.js +1 -1
  88. package/dist/esm/lib/patch-incorrect-lockfile.js +3 -3
  89. package/dist/esm/server/config.js +1 -1
  90. package/dist/esm/server/dev/hot-reloader-turbopack.js +2 -2
  91. package/dist/esm/server/dev/hot-reloader-webpack.js +1 -1
  92. package/dist/esm/server/lib/app-info-log.js +1 -1
  93. package/dist/esm/server/lib/start-server.js +1 -1
  94. package/dist/esm/shared/lib/errors/canary-only-config-error.js +1 -1
  95. package/dist/lib/patch-incorrect-lockfile.js +3 -3
  96. package/dist/server/config.js +1 -1
  97. package/dist/server/dev/hot-reloader-turbopack.js +2 -2
  98. package/dist/server/dev/hot-reloader-webpack.js +1 -1
  99. package/dist/server/lib/app-info-log.js +1 -1
  100. package/dist/server/lib/start-server.js +1 -1
  101. package/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  102. package/dist/telemetry/anonymous-meta.js +1 -1
  103. package/dist/telemetry/events/session-stopped.js +2 -2
  104. package/dist/telemetry/events/swc-load-failure.js +1 -1
  105. package/dist/telemetry/events/version.js +2 -2
  106. package/package.json +10 -252
  107. /package/dist/bundle-analyzer/_next/static/{ShlsHWUU3q2GhICNPxAIf → -wnBUGOTjuvnrsp1fJxyv}/_buildManifest.js +0 -0
  108. /package/dist/bundle-analyzer/_next/static/{ShlsHWUU3q2GhICNPxAIf → -wnBUGOTjuvnrsp1fJxyv}/_clientMiddlewareManifest.json +0 -0
  109. /package/dist/bundle-analyzer/_next/static/{ShlsHWUU3q2GhICNPxAIf → -wnBUGOTjuvnrsp1fJxyv}/_ssgManifest.js +0 -0
@@ -6,12 +6,43 @@ related:
6
6
  title: Next Steps
7
7
  description: Learn about other behavior changes when Cache Components is enabled.
8
8
  links:
9
+ - app/getting-started/caching
9
10
  - app/guides/preserving-ui-state
11
+ - app/api-reference/functions/generate-static-params
10
12
  - app/api-reference/config/next-config-js/cacheComponents
11
13
  ---
12
14
 
13
15
  When [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents) is enabled, route segment configs like `dynamic`, `revalidate`, and `fetchCache` are replaced by [`use cache`](/docs/app/api-reference/directives/use-cache) and [`cacheLife`](/docs/app/api-reference/functions/cacheLife).
14
16
 
17
+ Start by removing the route segment configs (`dynamic`, `revalidate`, `fetchCache`). With Cache Components enabled, Next.js surfaces uncached dynamic data as errors in development, naming the code to fix, most often uncached data to cache with [`use cache`](/docs/app/api-reference/directives/use-cache) or runtime data to wrap in [`<Suspense>`](https://react.dev/reference/react/Suspense).
18
+
19
+ Your existing `fetch` and `unstable_cache` caching keeps working as a separate layer, so let the errors guide what to change.
20
+
21
+ Some surfaces have their own steps:
22
+
23
+ - For routes with dynamic params, follow the [`generateStaticParams`](#generatestaticparams-and-dynamicparams) guidance.
24
+ - For metadata, follow the [`generateMetadata` and `generateViewport`](#generatemetadata-and-generateviewport) guidance.
25
+
26
+ The sections below cover each config and API and what to do with it under Cache Components.
27
+
28
+ ## Enable Cache Components
29
+
30
+ Cache Components requires Next.js 16. If you're on Next.js 15 or earlier, upgrade first by following the [version 16 upgrade guide](/docs/app/guides/upgrading/version-16). Coming from an older version, work through the [upgrade guides](/docs/app/guides/upgrading) to reach 16 before continuing.
31
+
32
+ Then enable the [`cacheComponents`](/docs/app/api-reference/config/next-config-js/cacheComponents) flag in `next.config.ts`:
33
+
34
+ ```ts filename="next.config.ts"
35
+ import type { NextConfig } from 'next'
36
+
37
+ const nextConfig: NextConfig = {
38
+ cacheComponents: true,
39
+ }
40
+
41
+ export default nextConfig
42
+ ```
43
+
44
+ > **Good to know:** If you were using `experimental.dynamicIO` or `experimental.useCache`, `cacheComponents` replaces them. See the [version 16 upgrade guide](/docs/app/guides/upgrading/version-16#experimentaldynamicio-and-experimentalusecache).
45
+
15
46
  ## `dynamic = "force-dynamic"`
16
47
 
17
48
  **Not needed.** All pages are dynamic by default.
@@ -176,18 +207,517 @@ export default async function Page() {
176
207
  }
177
208
  ```
178
209
 
210
+ ## `fetch` cache options
211
+
212
+ **Move `cache` and `next` options to `use cache`.**
213
+
214
+ Without Cache Components, you cache a request with `cache: 'force-cache'` and tune it with `next: { revalidate, tags }`.
215
+
216
+ With Cache Components, wrap the fetch in a [`use cache`](/docs/app/api-reference/directives/use-cache) function. Fetches inside that scope are cached automatically, and `revalidate` and `tags` become [`cacheLife`](/docs/app/api-reference/functions/cacheLife) and [`cacheTag`](/docs/app/api-reference/functions/cacheTag).
217
+
218
+ ```tsx filename="app/page.tsx" switcher
219
+ // Before
220
+ export default async function Page() {
221
+ const res = await fetch('https://api.example.com/data', {
222
+ cache: 'force-cache',
223
+ next: { revalidate: 3600, tags: ['data'] },
224
+ })
225
+ const data = await res.json()
226
+ return <div>...</div>
227
+ }
228
+ ```
229
+
230
+ ```jsx filename="app/page.js" switcher
231
+ // Before
232
+ export default async function Page() {
233
+ const res = await fetch('https://api.example.com/data', {
234
+ cache: 'force-cache',
235
+ next: { revalidate: 3600, tags: ['data'] },
236
+ })
237
+ const data = await res.json()
238
+ return <div>...</div>
239
+ }
240
+ ```
241
+
242
+ ```tsx filename="app/page.tsx" switcher
243
+ // After
244
+ import { cacheLife, cacheTag } from 'next/cache'
245
+
246
+ async function getData() {
247
+ 'use cache'
248
+ cacheLife('hours')
249
+ cacheTag('data')
250
+ const res = await fetch('https://api.example.com/data')
251
+ return res.json()
252
+ }
253
+
254
+ export default async function Page() {
255
+ const data = await getData()
256
+ return <div>...</div>
257
+ }
258
+ ```
259
+
260
+ ```jsx filename="app/page.js" switcher
261
+ // After
262
+ import { cacheLife, cacheTag } from 'next/cache'
263
+
264
+ async function getData() {
265
+ 'use cache'
266
+ cacheLife('hours')
267
+ cacheTag('data')
268
+ const res = await fetch('https://api.example.com/data')
269
+ return res.json()
270
+ }
271
+
272
+ export default async function Page() {
273
+ const data = await getData()
274
+ return <div>...</div>
275
+ }
276
+ ```
277
+
278
+ Note the persistence difference. The `fetch` Data Cache persists cached responses across deployments and across serverless instances.
279
+
280
+ `use cache` defaults to in-memory storage, so its entries are discarded when the serverless instance is destroyed and are scoped to a single deployment. Use [`use cache: remote`](/docs/app/api-reference/directives/use-cache-remote) or a [cache handler](/docs/app/api-reference/config/next-config-js/cacheHandlers) for storage that survives instance teardown. Even with durable storage, expect cached values to recompute after a new deployment.
281
+
282
+ ## `unstable_cache`
283
+
284
+ **Replace with `use cache`.**
285
+
286
+ `unstable_cache` is replaced by the [`use cache`](/docs/app/api-reference/directives/use-cache) directive.
287
+
288
+ Turn the wrapped function into a function with the `'use cache'` directive. The cache key is derived automatically from the arguments, so the key-parts array is no longer needed, and the `options` object maps to [`cacheLife`](/docs/app/api-reference/functions/cacheLife) and [`cacheTag`](/docs/app/api-reference/functions/cacheTag).
289
+
290
+ ```tsx filename="app/lib/data.ts" switcher
291
+ // Before
292
+ import { unstable_cache } from 'next/cache'
293
+ import { db } from '@/lib/db'
294
+
295
+ export const getUser = unstable_cache(
296
+ async (id: string) => {
297
+ return db.query.users.findFirst({ where: eq(users.id, id) })
298
+ },
299
+ ['user'], // cache key prefix
300
+ { tags: ['users'], revalidate: 3600 }
301
+ )
302
+ ```
303
+
304
+ ```js filename="app/lib/data.js" switcher
305
+ // Before
306
+ import { unstable_cache } from 'next/cache'
307
+ import { db } from '@/lib/db'
308
+
309
+ export const getUser = unstable_cache(
310
+ async (id) => {
311
+ return db.query.users.findFirst({ where: eq(users.id, id) })
312
+ },
313
+ ['user'], // cache key prefix
314
+ { tags: ['users'], revalidate: 3600 }
315
+ )
316
+ ```
317
+
318
+ ```tsx filename="app/lib/data.ts" switcher
319
+ // After
320
+ import { cacheLife, cacheTag } from 'next/cache'
321
+ import { db } from '@/lib/db'
322
+
323
+ export async function getUser(id: string) {
324
+ 'use cache'
325
+ cacheLife('hours')
326
+ cacheTag('users')
327
+ return db.query.users.findFirst({ where: eq(users.id, id) })
328
+ }
329
+ ```
330
+
331
+ ```js filename="app/lib/data.js" switcher
332
+ // After
333
+ import { cacheLife, cacheTag } from 'next/cache'
334
+ import { db } from '@/lib/db'
335
+
336
+ export async function getUser(id) {
337
+ 'use cache'
338
+ cacheLife('hours')
339
+ cacheTag('users')
340
+ return db.query.users.findFirst({ where: eq(users.id, id) })
341
+ }
342
+ ```
343
+
344
+ Like the `fetch` Data Cache, `unstable_cache` persists cached values across deployments and serverless instances, while `use cache` does not. See [`fetch` cache options](#fetch-cache-options) above for the storage details.
345
+
346
+ ## On-demand revalidation (`revalidateTag`, `revalidatePath`, `updateTag`)
347
+
348
+ On-demand invalidation still works by tagging cached data and expiring it after an event. Tag data with [`cacheTag`](/docs/app/api-reference/functions/cacheTag) inside a `use cache` function instead of the `fetch` `next.tags` option, then choose the invalidation API by the behavior you want:
349
+
350
+ - [`updateTag`](/docs/app/api-reference/functions/updateTag): for mutations whose result the user must see immediately (read-your-own-writes). Called from a Server Action, it expires the tag so the next request waits for fresh data instead of serving stale content.
351
+ - [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag): for stale-while-revalidate. Pass a cache profile like `'max'` to serve cached data while it refreshes in the background. Works in Server Actions and Route Handlers.
352
+ - [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath): unchanged from the previous caching model.
353
+
354
+ `updateTag` isn't exclusive to Cache Components (it also works with the previous caching model), but migrating is a good time to adopt it. After a mutation in a Server Action, reach for it when the user should see their own change right away.
355
+
356
+ ```tsx filename="app/actions.ts" switcher
357
+ 'use server'
358
+ import { updateTag } from 'next/cache'
359
+
360
+ export async function createPost(formData: FormData) {
361
+ // Create the post, then show it immediately on the next request
362
+ updateTag('posts')
363
+ }
364
+ ```
365
+
366
+ ```js filename="app/actions.js" switcher
367
+ 'use server'
368
+ import { updateTag } from 'next/cache'
369
+
370
+ export async function createPost(formData) {
371
+ // Create the post, then show it immediately on the next request
372
+ updateTag('posts')
373
+ }
374
+ ```
375
+
376
+ > **Good to know:** `updateTag` can only be called from a Server Action; calling it elsewhere throws. In Route Handlers or webhooks, use `revalidateTag` instead.
377
+
378
+ ## `unstable_noStore`
379
+
380
+ **Not needed.** `unstable_noStore` (`noStore()`) opts a component out of caching. With Cache Components, nothing is cached unless you add `use cache`, so you can remove it. If a component must run at request time, call [`connection()`](/docs/app/api-reference/functions/connection) before the work and wrap it in `<Suspense>`.
381
+
382
+ ```tsx filename="app/page.tsx" switcher
383
+ // Before
384
+ import { unstable_noStore as noStore } from 'next/cache'
385
+
386
+ export default async function Page() {
387
+ noStore()
388
+ const data = await db.query('...')
389
+ return <div>...</div>
390
+ }
391
+ ```
392
+
393
+ ```jsx filename="app/page.js" switcher
394
+ // Before
395
+ import { unstable_noStore as noStore } from 'next/cache'
396
+
397
+ export default async function Page() {
398
+ noStore()
399
+ const data = await db.query('...')
400
+ return <div>...</div>
401
+ }
402
+ ```
403
+
404
+ ```tsx filename="app/page.tsx" switcher
405
+ // After - uncached by default, just remove noStore()
406
+ export default async function Page() {
407
+ const data = await db.query('...')
408
+ return <div>...</div>
409
+ }
410
+ ```
411
+
412
+ ```jsx filename="app/page.js" switcher
413
+ // After - uncached by default, just remove noStore()
414
+ export default async function Page() {
415
+ const data = await db.query('...')
416
+ return <div>...</div>
417
+ }
418
+ ```
419
+
420
+ ## `generateStaticParams` and `dynamicParams`
421
+
422
+ One behavior changes for [dynamic routes](/docs/app/api-reference/file-conventions/dynamic-routes) when Cache Components is enabled.
423
+
424
+ ### `generateStaticParams` must return at least one param
425
+
426
+ **Returning an empty array now errors.** Without Cache Components, returning `[]` defers every path to the first runtime visit. With Cache Components, [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) must return at least one param so Next.js can prerender the route. An empty array raises [`empty-generate-static-params`](/docs/messages/empty-generate-static-params).
427
+
428
+ ```tsx filename="app/blog/[slug]/page.tsx" switcher
429
+ // Before - defer all paths to runtime
430
+ export async function generateStaticParams() {
431
+ return []
432
+ }
433
+ ```
434
+
435
+ ```jsx filename="app/blog/[slug]/page.js" switcher
436
+ // Before - defer all paths to runtime
437
+ export async function generateStaticParams() {
438
+ return []
439
+ }
440
+ ```
441
+
442
+ ```tsx filename="app/blog/[slug]/page.tsx" switcher
443
+ // After - return at least one param to prerender
444
+ export async function generateStaticParams() {
445
+ const posts = await fetch('https://.../posts').then((res) => res.json())
446
+ return posts.slice(0, 1).map((post) => ({ slug: post.slug }))
447
+ }
448
+ ```
449
+
450
+ ```jsx filename="app/blog/[slug]/page.js" switcher
451
+ // After - return at least one param to prerender
452
+ export async function generateStaticParams() {
453
+ const posts = await fetch('https://.../posts').then((res) => res.json())
454
+ return posts.slice(0, 1).map((post) => ({ slug: post.slug }))
455
+ }
456
+ ```
457
+
458
+ ## `cookies`, `headers`, and `searchParams`
459
+
460
+ **Wrap runtime data access in `<Suspense>`.** Without Cache Components, reading [`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers), or [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) opts the whole route into dynamic rendering. With Cache Components, accessing them outside a [`<Suspense>`](https://react.dev/reference/react/Suspense) boundary surfaces the [**blocking-route** insight](/docs/messages/blocking-route). Move the access into a component wrapped in `<Suspense>` so the rest of the page prerenders as a static shell and the dynamic part streams in at request time.
461
+
462
+ ```tsx filename="app/page.tsx" switcher
463
+ import { cookies } from 'next/headers'
464
+
465
+ // Before - reading cookies at the top makes the whole route dynamic
466
+ export default async function Page() {
467
+ const theme = (await cookies()).get('theme')?.value
468
+ return <Dashboard theme={theme} />
469
+ }
470
+ ```
471
+
472
+ ```jsx filename="app/page.js" switcher
473
+ import { cookies } from 'next/headers'
474
+
475
+ // Before - reading cookies at the top makes the whole route dynamic
476
+ export default async function Page() {
477
+ const theme = (await cookies()).get('theme')?.value
478
+ return <Dashboard theme={theme} />
479
+ }
480
+ ```
481
+
482
+ ```tsx filename="app/page.tsx" switcher
483
+ import { cookies } from 'next/headers'
484
+ import { Suspense } from 'react'
485
+
486
+ // After - the page prerenders; only Dashboard streams at request time
487
+ export default function Page() {
488
+ return (
489
+ <Suspense fallback={<p>Loading...</p>}>
490
+ <Dashboard />
491
+ </Suspense>
492
+ )
493
+ }
494
+
495
+ async function Dashboard() {
496
+ const theme = (await cookies()).get('theme')?.value
497
+ // ...
498
+ }
499
+ ```
500
+
501
+ ```jsx filename="app/page.js" switcher
502
+ import { cookies } from 'next/headers'
503
+ import { Suspense } from 'react'
504
+
505
+ // After - the page prerenders; only Dashboard streams at request time
506
+ export default function Page() {
507
+ return (
508
+ <Suspense fallback={<p>Loading...</p>}>
509
+ <Dashboard />
510
+ </Suspense>
511
+ )
512
+ }
513
+
514
+ async function Dashboard() {
515
+ const theme = (await cookies()).get('theme')?.value
516
+ // ...
517
+ }
518
+ ```
519
+
520
+ Your page receives `params` and `searchParams` as props, and both are promises. Apply the same pattern: pass the promise straight through to the `<Suspense>`-wrapped component as a prop and `await` it there, rather than at the top of the page. You can also unwrap the promise inline with `.then()` and pass a plain value down; see [Streaming](/docs/app/guides/streaming#push-dynamic-access-down) for a similar pattern.
521
+
522
+ ```tsx filename="app/page.tsx" switcher
523
+ import { Suspense } from 'react'
524
+
525
+ export default function Page({ searchParams }: PageProps<'/'>) {
526
+ return (
527
+ <Suspense fallback={<p>Loading...</p>}>
528
+ <Results searchParams={searchParams} />
529
+ </Suspense>
530
+ )
531
+ }
532
+
533
+ async function Results({ searchParams }: Pick<PageProps<'/'>, 'searchParams'>) {
534
+ const { query } = await searchParams
535
+ // ...
536
+ }
537
+ ```
538
+
539
+ ```jsx filename="app/page.js" switcher
540
+ import { Suspense } from 'react'
541
+
542
+ export default function Page({ searchParams }) {
543
+ return (
544
+ <Suspense fallback={<p>Loading...</p>}>
545
+ <Results searchParams={searchParams} />
546
+ </Suspense>
547
+ )
548
+ }
549
+
550
+ async function Results({ searchParams }) {
551
+ const { query } = await searchParams
552
+ // ...
553
+ }
554
+ ```
555
+
556
+ ## Route Handlers (`GET`)
557
+
558
+ **Replace `dynamic = 'force-static'` with `use cache`.**
559
+
560
+ Without Cache Components, a `GET` [Route Handler](/docs/app/api-reference/file-conventions/route) is dynamic unless you opt into caching with `export const dynamic = 'force-static'`. With Cache Components, `GET` handlers follow the same model as pages: they prerender when they don't access uncached or runtime data, and you cache uncached data with `use cache`. Remove the `dynamic` config and move the data access into a separate function marked with `use cache`. The directive can't be applied to the `GET` export itself, so the handler calls a cached helper.
561
+
562
+ ```ts filename="app/api/products/route.ts" switcher
563
+ // Before
564
+ export const dynamic = 'force-static'
565
+
566
+ export async function GET() {
567
+ const products = await db.query('SELECT * FROM products')
568
+ return Response.json(products)
569
+ }
570
+ ```
571
+
572
+ ```js filename="app/api/products/route.js" switcher
573
+ // Before
574
+ export const dynamic = 'force-static'
575
+
576
+ export async function GET() {
577
+ const products = await db.query('SELECT * FROM products')
578
+ return Response.json(products)
579
+ }
580
+ ```
581
+
582
+ ```ts filename="app/api/products/route.ts" switcher
583
+ // After
584
+ import { cacheLife } from 'next/cache'
585
+
586
+ export async function GET() {
587
+ const products = await getProducts()
588
+ return Response.json(products)
589
+ }
590
+
591
+ async function getProducts() {
592
+ 'use cache'
593
+ cacheLife('hours')
594
+ return db.query('SELECT * FROM products')
595
+ }
596
+ ```
597
+
598
+ ```js filename="app/api/products/route.js" switcher
599
+ // After
600
+ import { cacheLife } from 'next/cache'
601
+
602
+ export async function GET() {
603
+ const products = await getProducts()
604
+ return Response.json(products)
605
+ }
606
+
607
+ async function getProducts() {
608
+ 'use cache'
609
+ cacheLife('hours')
610
+ return db.query('SELECT * FROM products')
611
+ }
612
+ ```
613
+
614
+ > **Good to know:** Reading uncached or runtime data in a `GET` handler bails out of prerendering by **throwing**. A `try/catch` you already have around other operations will catch that bail-out. If the `catch` block logs the error, it adds noise to the build output. Set `experimental.hideLogsAfterAbort: true` to hide logs emitted after a bail-out.
615
+
616
+ ## `generateMetadata` and `generateViewport`
617
+
618
+ **Cache external data with `use cache`, or mark intentionally dynamic pages.** Under Cache Components, [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) and [`generateViewport`](/docs/app/api-reference/functions/generate-viewport) follow the same rules as components. If they read runtime data (`cookies()`, `headers()`, `params`, `searchParams`) or fetch uncached data while the rest of the page is otherwise prerenderable, Next.js raises an error so the choice is explicit. If the metadata depends on external but not runtime data, add `use cache`.
619
+
620
+ ```tsx filename="app/page.tsx" switcher
621
+ // Before
622
+ export async function generateMetadata() {
623
+ const { title, description } = await db.query('site-metadata')
624
+ return { title, description }
625
+ }
626
+ ```
627
+
628
+ ```tsx filename="app/page.tsx" switcher
629
+ // After - cache external data
630
+ export async function generateMetadata() {
631
+ 'use cache'
632
+ const { title, description } = await db.query('site-metadata')
633
+ return { title, description }
634
+ }
635
+ ```
636
+
637
+ If the metadata genuinely needs runtime data, you can't wrap `generateMetadata` in `<Suspense>`. Instead, add a dynamic marker component to the page so the static content still prerenders while the metadata streams in.
638
+
639
+ ```tsx filename="app/page.tsx" switcher
640
+ import { Suspense } from 'react'
641
+ import { connection } from 'next/server'
642
+
643
+ export async function generateMetadata() {
644
+ // reads runtime data
645
+ return { title: 'Personalized Title' }
646
+ }
647
+
648
+ async function DynamicMarker() {
649
+ return (
650
+ <Suspense>
651
+ <Connection />
652
+ </Suspense>
653
+ )
654
+ }
655
+
656
+ async function Connection() {
657
+ await connection()
658
+ return null
659
+ }
660
+
661
+ export default function Page() {
662
+ return (
663
+ <>
664
+ <article>Static content</article>
665
+ <DynamicMarker />
666
+ </>
667
+ )
668
+ }
669
+ ```
670
+
671
+ See [`generateMetadata` with Cache Components](/docs/app/api-reference/functions/generate-metadata#with-cache-components) and [`generateViewport` with Cache Components](/docs/app/api-reference/functions/generate-viewport#with-cache-components) for the full set of fix options and trade-offs.
672
+
179
673
  ## `runtime = 'edge'`
180
674
 
181
675
  **Not supported.** Cache Components requires the Node.js runtime. Switch to the Node.js runtime (the default) by removing the `runtime = 'edge'` export. If you need edge behavior for specific routes, use [Proxy](/docs/app/api-reference/file-conventions/proxy) instead.
182
676
 
677
+ ## `experimental_ppr`
678
+
679
+ **Removed. Enable `cacheComponents` instead.** Next.js 16 removes the experimental Partial Prerendering flag (`experimental.ppr`) and the `experimental_ppr` route segment config. Partial Prerendering is now part of [Cache Components](/docs/app/api-reference/config/next-config-js/cacheComponents), so remove `experimental.ppr` from `next.config` and `experimental_ppr` from your segments. A [codemod](/docs/app/guides/upgrading/codemods#remove-experimental_ppr-route-segment-config-from-app-router-pages-and-layouts) removes the segment config for you.
680
+
681
+ ```tsx filename="app/page.tsx" switcher
682
+ // Before - no longer needed
683
+ export const experimental_ppr = true
684
+
685
+ export default function Page() {
686
+ return <div>...</div>
687
+ }
688
+ ```
689
+
690
+ ```jsx filename="app/page.js" switcher
691
+ // Before - no longer needed
692
+ export const experimental_ppr = true
693
+
694
+ export default function Page() {
695
+ return <div>...</div>
696
+ }
697
+ ```
698
+
699
+ ```tsx filename="app/page.tsx" switcher
700
+ // After - remove it; cacheComponents enables Partial Prerendering
701
+ export default function Page() {
702
+ return <div>...</div>
703
+ }
704
+ ```
705
+
706
+ ```jsx filename="app/page.js" switcher
707
+ // After - remove it; cacheComponents enables Partial Prerendering
708
+ export default function Page() {
709
+ return <div>...</div>
710
+ }
711
+ ```
712
+
183
713
  ## UI state preservation
184
714
 
185
715
  **Component state now persists across navigations.** With Cache Components, Next.js preserves routes using React's [`<Activity>`](https://react.dev/reference/react/Activity) component in [`"hidden"`](https://react.dev/reference/react/Activity#activity) mode instead of unmounting them. Effects clean up and re-run normally, but `useState` values, form inputs, and scroll position are no longer reset when navigating away and back.
186
716
 
187
717
  If your code relied on unmounting to clear state, you may need to add explicit reset logic:
188
718
 
189
- - **Dropdowns and popovers** stay open when navigating back. Close them in a `useLayoutEffect` cleanup function.
190
- - **Dialogs with initialization logic** Effects that depend on dialog state (like focusing an input) won't re-fire if the state was preserved. Derive dialog state from the URL instead.
191
- - **Forms after submission** input values and `useActionState` results (success/error messages) persist when returning. Reset in the submit handler or user action when possible, otherwise use a cleanup effect.
719
+ - **Dropdowns and popovers**: stay open when navigating back. Close them in a `useLayoutEffect` cleanup function.
720
+ - **Dialogs with initialization logic**: Effects that depend on dialog state (like focusing an input) won't re-fire if the state was preserved. Derive dialog state from the URL instead.
721
+ - **Forms after submission**: input values and `useActionState` results (success/error messages) persist when returning. Reset in the submit handler or user action when possible, otherwise use a cleanup effect.
192
722
 
193
723
  See [Preserving UI state across navigations](/docs/app/guides/preserving-ui-state) for detailed examples of each pattern.
@@ -113,7 +113,7 @@ export async function proxy(request) {
113
113
 
114
114
  ## Linking between zones
115
115
 
116
- Links to paths in a different zone should use an `a` tag instead of the Next.js [`<Link>`](/docs/pages/api-reference/components/link) component. This is because Next.js will try to prefetch and soft navigate to any relative path in `<Link>` component, which will not work across zones.
116
+ Links to paths in a different zone should use an `a` tag instead of the Next.js [`<Link>`](/docs/app/api-reference/components/link) component. This is because Next.js will try to prefetch and soft navigate to any relative path in `<Link>` component, which will not work across zones.
117
117
 
118
118
  ## Sharing code
119
119
 
@@ -281,7 +281,7 @@ const nextConfig = {
281
281
  module.exports = nextConfig
282
282
  ```
283
283
 
284
- To automatically bundle all packages, you can use the [`bundlePagesRouterDependencies`](/docs/pages/api-reference/config/next-config-js/bundlePagesRouterDependencies) option in your `next.config.js`.
284
+ To automatically bundle all packages, you can use the [`bundlePagesRouterDependencies`](/docs/pages/api-reference/config/next-config-js/bundlePagesRouterDependencies) option in your `next.config.js`. This option defaults to `false`.
285
285
 
286
286
  ```js filename="next.config.js"
287
287
  /** @type {import('next').NextConfig} */
@@ -49,10 +49,10 @@ export default function NavLink() {
49
49
  }
50
50
  ```
51
51
 
52
- | **Context** | **Prefetched payload** | **Client Cache TTL** |
53
- | ----------------- | -------------------------------- | ------------------------------------------------------------------------------ |
54
- | No `loading.js` | Entire page | Until app reload |
55
- | With `loading.js` | Layout to first loading boundary | 30s ([configurable](/docs/app/api-reference/config/next-config-js/staleTimes)) |
52
+ | **Context** | **Prefetched payload** | **Client Cache TTL** |
53
+ | ----------------- | -------------------------------- | ------------------------------------------------------------------------------------------------- |
54
+ | No `loading.js` | Entire page | 5 min ([`staleTimes.static`](/docs/app/api-reference/config/next-config-js/staleTimes)) |
55
+ | With `loading.js` | Layout to first loading boundary | Off by default ([`staleTimes.dynamic`](/docs/app/api-reference/config/next-config-js/staleTimes)) |
56
56
 
57
57
  Automatic prefetching runs only in production. Disable with `prefetch={false}` or use the wrapper in [Disabled Prefetch](#disabled-prefetch).
58
58
 
@@ -78,7 +78,7 @@ export function PricingCard() {
78
78
  }
79
79
  ```
80
80
 
81
- If the intent is to prefetch a URL when a component loads, see the extending or rejecting a link [example].
81
+ To prefetch a URL when a component loads, see [Extending or ejecting link](#extending-or-ejecting-link).
82
82
 
83
83
  ## Hover-triggered prefetch
84
84
 
@@ -4,7 +4,7 @@ nav_title: Preventing Flash
4
4
  description: Learn how to correct server-rendered content before the browser paints, avoiding visible flash when the page hydrates.
5
5
  ---
6
6
 
7
- User preferences, browser settings, and client-side storage aren't available during server rendering. The server emits a reasonable default, but any UI that depends on client-only state (locale, timezone, theme, persisted interactions) needs to be updated before the user sees it.
7
+ User preferences, browser settings, and client-side storage aren't available during server rendering. The server emits a reasonable default, but any UI that depends on client-only state (locale, time zone, theme, persisted interactions) needs to be updated before the user sees it.
8
8
 
9
9
  Some common approaches to this problem:
10
10
 
@@ -26,7 +26,7 @@ Use Chrome DevTools [Sensors](https://developer.chrome.com/docs/devtools/sensors
26
26
 
27
27
  ## Dates and formatting
28
28
 
29
- A UTC timestamp like `2026-06-15T18:00:00Z` represents a fixed point in time, but how it is formatted for display depends on the user's locale and timezone. `toLocaleDateString()` and `Intl.DateTimeFormat` on the server use the server's settings, which may not match the user's.
29
+ A UTC timestamp like `2026-06-15T18:00:00Z` represents a fixed point in time, but how it is formatted for display depends on the user's locale and time zone. `toLocaleDateString()` and `Intl.DateTimeFormat` on the server use the server's settings, which may not match the user's.
30
30
 
31
31
  ### The problem
32
32
 
@@ -454,7 +454,7 @@ The inline script and the lazy `useState` initializer both read from `localStora
454
454
 
455
455
  ### Why not read from headers or cookies at request time?
456
456
 
457
- Reading `Accept-Language` with `await headers()` lets the server format per-request. With Cache Components, you can wrap just the date in a Suspense fallback so the rest of the page stays static. But you'd need to show a fallback instead of immediate content, and `Accept-Language` doesn't include timezone information.
457
+ Reading `Accept-Language` with `await headers()` lets the server format per-request. With Cache Components, you can wrap just the date in a Suspense fallback so the rest of the page stays static. But you'd need to show a fallback instead of immediate content, and `Accept-Language` doesn't include time zone information.
458
458
 
459
459
  ## Next steps
460
460
 
@@ -99,7 +99,7 @@ However, if this component is rendered at request time, fetching its data will d
99
99
 
100
100
  Even though the header is rendered instantly, it can't be sent to the browser until the product list has finished fetching.
101
101
 
102
- To protect us from this performance cliff, Next.js will show us a [warning](/docs/messages/blocking-route) the first time we **await** data: `Blocking data was accessed outside of Suspense`
102
+ To protect us from this performance cliff, the first time we **await** this uncached data Next.js shows a [warning](/docs/messages/blocking-route): accessing uncached data outside of `<Suspense>` prevents the route from being prerendered.
103
103
 
104
104
  At this point, we have to decide how to **unblock** the response. Either:
105
105
 
@@ -195,7 +195,7 @@ Although the `worker` strategy does not require any additional configuration to
195
195
 
196
196
  If you would like to add additional configuration options, you can include it within the `<Head />` component used in a [custom `_document.js`](/docs/pages/building-your-application/routing/custom-document):
197
197
 
198
- ```jsx filename="_pages/document.jsx"
198
+ ```jsx filename="pages/_document.js"
199
199
  import { Html, Head, Main, NextScript } from 'next/document'
200
200
 
201
201
  export default function Document() {