From 2f6054f419aa622bbee06198ca0517cc49c797a0 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:41:49 +0100 Subject: [PATCH] Docs: RC Updates (#70031) **Do not merge.** - [x] `staticgeneration*` options - [x] `next/form` now available in pages, `prefetch` prop added to app - [x] ~`next/after` stable~ reverted changes - [x] `instrumentation.js` stable - [x] More secure server actions - [x] Update turbopack docs - [x] https://nextjs.org/docs/architecture/turbopack - [x] https://nextjs.org/docs/app/api-reference/next-config-js/turbo - [x] Move `turbo` config option out of experimental - [x] Document `connection()` - [x] De-prioritize `unstable_noStore` - add note about using `connection()` - [x] Replace examples of `noStore` with `connection` - [x] Async APIs - [x] Reference Pages - [x] `cookies` - [x] `headers` - [x] `draftMode` - [x] `page.js` (`searchParams` and `params` prop) - [x] `layout.js` (`params` prop) - [x] `route.js` (`params` prop) - [x] `default.js` (`params` prop) - [x] Clarify `use` usage https://vercel.slack.com/archives/C06N3AXRQ82/p1726483924090979?thread_ts=1726250070.321229&cid=C06N3AXRQ82 - [x] Update **examples** throughout the docs: - [x] `cookies` - [x] `headers` - [x] `draftMode` - [x] `searchParams` - [x] `params` - [x] Update Dynamic API section in the Server Components Page: https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering - [x] Update Dynamic API mentions in Route Handler page: https://nextjs.org/docs/app/building-your-application/routing/route-handlers#dynamic-functions - [x] Update upgrade guide for Next 15 RC: https://nextjs.org/docs/app/building-your-application/upgrading/version-15 - [x] Document codemods for async APIs. PR: https://github.com/vercel/next.js/pull/69572 - [x] Document geo codemod. Done here: https://github.com/vercel/next.js/pull/70064 Closes: https://linear.app/vercel/issue/DOC-3018/nextjs-15-rc-2 --------- Co-authored-by: Rich Haines Co-authored-by: Ahmed Abdelbaset Co-authored-by: Nikhil S --- .../01-routing/13-route-handlers.mdx | 34 ++-- .../02-data-fetching/01-fetching.mdx | 6 +- .../03-server-actions-and-mutations.mdx | 42 +++- .../03-rendering/01-server-components.mdx | 36 ++-- .../03-rendering/04-partial-prerendering.mdx | 26 +-- .../04-caching/index.mdx | 42 ++-- .../06-optimizing/04-metadata.mdx | 8 +- .../06-optimizing/09-instrumentation.mdx | 2 - .../06-optimizing/10-open-telemetry.mdx | 2 - .../03-environment-variables.mdx | 37 +++- .../07-configuring/11-draft-mode.mdx | 16 +- .../15-content-security-policy.mdx | 8 +- .../09-authentication/index.mdx | 64 +++--- .../10-deploying/01-production-checklist.mdx | 2 +- .../10-deploying/index.mdx | 41 +++- .../11-upgrading/01-codemods.mdx | 2 +- .../11-upgrading/02-version-15.mdx | 2 +- .../11-upgrading/04-app-router-migration.mdx | 16 +- .../02-api-reference/01-components/form.mdx | 90 ++++++++- .../01-metadata/app-icons.mdx | 4 +- .../01-metadata/manifest.mdx | 2 +- .../01-metadata/opengraph-image.mdx | 4 +- .../01-metadata/robots.mdx | 2 +- .../01-metadata/sitemap.mdx | 2 +- .../02-file-conventions/default.mdx | 33 +++- .../02-file-conventions/instrumentation.mdx | 37 +--- .../02-file-conventions/layout.mdx | 158 +++++++++++---- .../02-file-conventions/not-found.mdx | 4 +- .../02-file-conventions/page.mdx | 182 +++++++++++++++--- .../route-segment-config.mdx | 22 ++- .../02-file-conventions/route.mdx | 102 +++++++--- .../04-functions/connection.mdx | 57 ++++++ .../02-api-reference/04-functions/cookies.mdx | 100 +++++----- .../04-functions/draft-mode.mdx | 55 ++++-- .../04-functions/generate-image-metadata.mdx | 4 +- .../04-functions/generate-metadata.mdx | 4 +- .../04-functions/generate-static-params.mdx | 28 +-- .../02-api-reference/04-functions/headers.mdx | 102 +++------- .../04-functions/unstable_after.mdx | 52 +++-- .../04-functions/unstable_noStore.mdx | 4 +- .../04-functions/unstable_rethrow.mdx | 6 +- .../04-functions/use-router.mdx | 2 +- .../05-next-config-js/devIndicators.mdx | 2 +- .../05-next-config-js/instrumentationHook.mdx | 28 --- .../05-next-config-js/reactCompiler.mdx | 2 +- .../serverComponentsHmrCache.mdx | 2 +- .../05-next-config-js/staticGeneration.mdx | 41 ++++ .../05-next-config-js/turbo.mdx | 139 +++++++------ .../02-api-reference/01-components/form.mdx | 7 + .../04-next-config-js/instrumentationHook.mdx | 7 - docs/04-architecture/turbopack.mdx | 4 +- errors/app-static-to-dynamic-error.mdx | 4 +- errors/sync-dynamic-apis.mdx | 2 +- 53 files changed, 1088 insertions(+), 592 deletions(-) create mode 100644 docs/02-app/02-api-reference/04-functions/connection.mdx delete mode 100644 docs/02-app/02-api-reference/05-next-config-js/instrumentationHook.mdx create mode 100644 docs/02-app/02-api-reference/05-next-config-js/staticGeneration.mdx create mode 100644 docs/03-pages/02-api-reference/01-components/form.mdx delete mode 100644 docs/03-pages/02-api-reference/04-next-config-js/instrumentationHook.mdx diff --git a/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx b/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx index 292f7719ee78b..628aae57f26bd 100644 --- a/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx +++ b/docs/02-app/01-building-your-application/01-routing/13-route-handlers.mdx @@ -84,7 +84,7 @@ export async function GET() { ### Special Route Handlers -Special Route Handlers like [`sitemap.ts`](/docs/app/api-reference/file-conventions/metadata/sitemap), [`opengraph-image.tsx`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), and [`icon.tsx`](/docs/app/api-reference/file-conventions/metadata/app-icons), and other [metadata files](/docs/app/api-reference/file-conventions/metadata) remain static by default unless they use dynamic functions or dynamic config options. +Special Route Handlers like [`sitemap.ts`](/docs/app/api-reference/file-conventions/metadata/sitemap), [`opengraph-image.tsx`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), and [`icon.tsx`](/docs/app/api-reference/file-conventions/metadata/app-icons), and other [metadata files](/docs/app/api-reference/file-conventions/metadata) remain static by default unless they use Dynamic APIs or dynamic config options. ### Route Resolution @@ -141,11 +141,7 @@ export async function GET() { } ``` -### Dynamic Functions - -Route Handlers can be used with dynamic functions from Next.js, like [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers). - -#### Cookies +### Cookies You can read or set cookies with [`cookies`](/docs/app/api-reference/functions/cookies) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function. @@ -155,7 +151,7 @@ Alternatively, you can return a new `Response` using the [`Set-Cookie`](https:// import { cookies } from 'next/headers' export async function GET(request: Request) { - const cookieStore = cookies() + const cookieStore = await cookies() const token = cookieStore.get('token') return new Response('Hello, Next.js!', { @@ -169,7 +165,7 @@ export async function GET(request: Request) { import { cookies } from 'next/headers' export async function GET(request) { - const cookieStore = cookies() + const cookieStore = await cookies() const token = cookieStore.get('token') return new Response('Hello, Next.js!', { @@ -195,7 +191,7 @@ export async function GET(request) { } ``` -#### Headers +### Headers You can read headers with [`headers`](/docs/app/api-reference/functions/headers) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function. @@ -205,7 +201,7 @@ This `headers` instance is read-only. To set headers, you need to return a new ` import { headers } from 'next/headers' export async function GET(request: Request) { - const headersList = headers() + const headersList = await headers() const referer = headersList.get('referer') return new Response('Hello, Next.js!', { @@ -219,7 +215,7 @@ export async function GET(request: Request) { import { headers } from 'next/headers' export async function GET(request) { - const headersList = headers() + const headersList = await headers() const referer = headersList.get('referer') return new Response('Hello, Next.js!', { @@ -272,23 +268,23 @@ Route Handlers can use [Dynamic Segments](/docs/app/building-your-application/ro ```ts filename="app/items/[slug]/route.ts" switcher export async function GET( request: Request, - { params }: { params: { slug: string } } + { params }: { params: Promise<{ slug: string }> } ) { - const slug = params.slug // 'a', 'b', or 'c' + const slug = (await params).slug // 'a', 'b', or 'c' } ``` ```js filename="app/items/[slug]/route.js" switcher export async function GET(request, { params }) { - const slug = params.slug // 'a', 'b', or 'c' + const slug = (await params).slug // 'a', 'b', or 'c' } ``` -| Route | Example URL | `params` | -| --------------------------- | ----------- | --------------- | -| `app/items/[slug]/route.js` | `/items/a` | `{ slug: 'a' }` | -| `app/items/[slug]/route.js` | `/items/b` | `{ slug: 'b' }` | -| `app/items/[slug]/route.js` | `/items/c` | `{ slug: 'c' }` | +| Route | Example URL | `params` | +| --------------------------- | ----------- | ------------------------ | +| `app/items/[slug]/route.js` | `/items/a` | `Promise<{ slug: 'a' }>` | +| `app/items/[slug]/route.js` | `/items/b` | `Promise<{ slug: 'b' }>` | +| `app/items/[slug]/route.js` | `/items/c` | `Promise<{ slug: 'c' }>` | ### URL Query Parameters diff --git a/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx b/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx index d4344d961a7a3..c3270ed0080a2 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/01-fetching.mdx @@ -87,7 +87,7 @@ export default async function Page() { } ``` -If you are not using any [dynamic functions](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in this route, it will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). +If you are not using any [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in this route, it will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). If you do _not_ want to cache the response from `fetch`, you can do the following: @@ -129,7 +129,7 @@ export default async function Page() { } ``` -If you are not using any [dynamic functions](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in this route, it will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). +If you are not using any [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in this route, it will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). To prevent the page from prerendering, you can add the following to your file: @@ -137,7 +137,7 @@ To prevent the page from prerendering, you can add the following to your file: export const dynamic = 'force-dynamic' ``` -However, you will commonly use functions like `cookies()`, `headers()`, or reading the incoming `searchParams` from the page props, which will automatically make the page render dynamically. In this case, you do _not_ need to explicitly use `force-dynamic`. +However, you will commonly use functions like `cookies`, `headers`, or reading the incoming `searchParams` from the page props, which will automatically make the page render dynamically. In this case, you do _not_ need to explicitly use `force-dynamic`. ### Fetching data on the client diff --git a/docs/02-app/01-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx b/docs/02-app/01-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx index 1c0abc01d116f..6fc0488ee6387 100644 --- a/docs/02-app/01-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx +++ b/docs/02-app/01-building-your-application/02-data-fetching/03-server-actions-and-mutations.mdx @@ -838,14 +838,16 @@ You can `get`, `set`, and `delete` cookies inside a Server Action using the [`co import { cookies } from 'next/headers' export async function exampleAction() { + const cookieStore = await cookies() + // Get cookie - const value = cookies().get('name')?.value + cookieStore.get('name')?.value // Set cookie - cookies().set('name', 'Delba') + cookieStore.set('name', 'Delba') // Delete cookie - cookies().delete('name') + cookieStore.delete('name') } ``` @@ -856,13 +858,16 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - const value = cookies().get('name')?.value + const cookieStore = await cookies() + + // Get cookie + cookieStore.get('name')?.value // Set cookie - cookies().set('name', 'Delba') + cookieStore.set('name', 'Delba') // Delete cookie - cookies().delete('name') + cookieStore.delete('name') } ``` @@ -870,9 +875,32 @@ See [additional examples](/docs/app/api-reference/functions/cookies#deleting-coo ## Security +By default, when a Server Action is created and exported, it creates a public HTTP endpoint +and should be treated with the same security assumptions and authorization checks. This means, even if a Server Action or utility function is not imported elsewhere in your code, it’s still a publicly accessible. + +To improve security, Next.js has the following built-in features: + +- **Dead code elimination:** Unused Server Actions won’t have their IDs exposed to the client-side JavaScript bundle, reducing bundle size and improving performance. +- **Secure action IDs:** Next.js creates unguessable, non-deterministic IDs to allow the client to reference and call the Server Action. These IDs are periodically recalculated for enhanced security. + +```jsx +// app/actions.js +'use server' + +// This action **is** used in our application, so Next.js +// will create a secure ID to allow the client to reference +// and call the Server Action. +export async function updateUserAction(formData) {} + +// This action **is not** used in our application, so Next.js +// will automatically remove this code during `next build` +// and will not create a public endpoint. +export async function deleteUserAction(formData) {} +``` + ### Authentication and authorization -You should treat Server Actions as you would public-facing API endpoints, and ensure that the user is authorized to perform the action. For example: +You should ensure that the user is authorized to perform the action. For example: ```tsx filename="app/actions.ts" 'use server' diff --git a/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx b/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx index 87a818125484f..f8031dc8e3557 100644 --- a/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx +++ b/docs/02-app/01-building-your-application/03-rendering/01-server-components.mdx @@ -62,16 +62,12 @@ There are three subsets of server rendering: Static, Dynamic, and Streaming. ### Static Rendering (Default) -{/* Static Rendering Diagram */} - With Static Rendering, routes are rendered at **build time**, or in the background after [data revalidation](/docs/app/building-your-application/data-fetching/incremental-static-regeneration). The result is cached and can be pushed to a [Content Delivery Network (CDN)](https://developer.mozilla.org/docs/Glossary/CDN). This optimization allows you to share the result of the rendering work between users and server requests. Static rendering is useful when a route has data that is not personalized to the user and can be known at build time, such as a static blog post or a product page. ### Dynamic Rendering -{/* Dynamic Rendering Diagram */} - With Dynamic Rendering, routes are rendered for each user at **request time**. Dynamic rendering is useful when a route has data that is personalized to the user or has information that can only be known at request time, such as cookies or the URL's search params. @@ -86,30 +82,30 @@ Dynamic rendering is useful when a route has data that is personalized to the us #### Switching to Dynamic Rendering -During rendering, if a [dynamic function](#dynamic-functions) or uncached data request is discovered, Next.js will switch to dynamically rendering the whole route. This table summarizes how dynamic functions and data caching affect whether a route is statically or dynamically rendered: +During rendering, if a [Dynamic API](#dynamic-apis) or uncached data request is discovered, Next.js will switch to dynamically rendering the whole route. This table summarizes how Dynamic APIs and data caching affect whether a route is statically or dynamically rendered: -| Dynamic Functions | Data | Route | -| ----------------- | ---------- | -------------------- | -| No | Cached | Statically Rendered | -| Yes | Cached | Dynamically Rendered | -| No | Not Cached | Dynamically Rendered | -| Yes | Not Cached | Dynamically Rendered | +| Dynamic APIs | Data | Route | +| ------------ | ---------- | -------------------- | +| No | Cached | Statically Rendered | +| Yes | Cached | Dynamically Rendered | +| No | Not Cached | Dynamically Rendered | +| Yes | Not Cached | Dynamically Rendered | In the table above, for a route to be fully static, all data must be cached. However, you can have a dynamically rendered route that uses both cached and uncached data fetches. As a developer, you do not need to choose between static and dynamic rendering as Next.js will automatically choose the best rendering strategy for each route based on the features and APIs used. Instead, you choose when to [cache](/docs/app/building-your-application/data-fetching/fetching) or [revalidate specific data](/docs/app/building-your-application/data-fetching/incremental-static-regeneration), and you may choose to [stream](#streaming) parts of your UI. -#### Dynamic Functions - -Dynamic functions rely on information that can only be known at request time such as a user's cookies, current requests headers, or the URL's search params. In Next.js, these dynamic APIs are: +### Dynamic APIs -- [`cookies()`](/docs/app/api-reference/functions/cookies) -- [`headers()`](/docs/app/api-reference/functions/headers) -- [`unstable_noStore()`](/docs/app/api-reference/functions/unstable_noStore) -- [`unstable_after()`](/docs/app/api-reference/functions/unstable_after): -- [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional) +Dynamic APIs rely on information that can only be known at request time (and not ahead of time during prerendering). Using any of these APIs signals the developer's intention and will opt the whole route into dynamic rendering at the request time. These APIs include: -Using any of these functions will opt the whole route into dynamic rendering at request time. +- [`cookies`](https://www.notion.so/docs/app/api-reference/functions/cookies) +- [`headers`](https://www.notion.so/docs/app/api-reference/functions/headers) +- [`unstable_noStore`](https://www.notion.so/docs/app/api-reference/functions/unstable_noStore) +- [`unstable_after`](https://www.notion.so/docs/app/api-reference/functions/unstable_after) +- [`connection`](https://www.notion.so/docs/app/api-reference/functions/connection) +- [`draftMode`](https://www.notion.so/docs/app/api-reference/functions/draft-mode) +- [`searchParams` prop](https://www.notion.so/docs/app/api-reference/file-conventions/page#searchparams-optional) ### Streaming diff --git a/docs/02-app/01-building-your-application/03-rendering/04-partial-prerendering.mdx b/docs/02-app/01-building-your-application/03-rendering/04-partial-prerendering.mdx index 601a6bc87ae6d..2e51f677cb32e 100644 --- a/docs/02-app/01-building-your-application/03-rendering/04-partial-prerendering.mdx +++ b/docs/02-app/01-building-your-application/03-rendering/04-partial-prerendering.mdx @@ -127,15 +127,15 @@ module.exports = nextConfig ## Dynamic Components -When creating the prerender for your route during `next build`, Next.js requires that dynamic functions are wrapped with React Suspense. The `fallback` is then included in the prerender. +When creating the prerender for your route during `next build`, Next.js requires that Dynamic APIs are wrapped with React Suspense. The `fallback` is then included in the prerender. -For example, using functions like `cookies()` or `headers()`: +For example, using functions like `cookies` or `headers`: ```jsx filename="app/user.js" switcher import { cookies } from 'next/headers' -export function User() { - const session = cookies().get('session')?.value +export async function User() { + const session = (await cookies()).get('session')?.value return '...' } ``` @@ -143,8 +143,8 @@ export function User() { ```tsx filename="app/user.tsx" switcher import { cookies } from 'next/headers' -export function User() { - const session = cookies().get('session')?.value +export async function User() { + const session = (await cookies()).get('session')?.value return '...' } ``` @@ -224,17 +224,21 @@ export default function Page({ searchParams }) { Inside of the table component, accessing the value from `searchParams` will make the component run dynamically: ```tsx filename="app/table.tsx" switcher -export function Table({ searchParams }: { searchParams: { sort: string } }) { - const sort = searchParams.sort === 'true' +export async function Table({ + searchParams, +}: { + searchParams: Promise<{ sort: string }> +}) { + const sort = (await searchParams).sort === 'true' return '...' } ``` ```jsx filename="app/table.js" switcher -export function Table({ searchParams }: { - searchParams: { sort: string } +export async function Table({ searchParams }: { + searchParams: Promise<{ sort: string }> }) { - const sort = searchParams.sort === 'true'; + const sort = (await searchParams).sort === 'true'; return '...' } ``` diff --git a/docs/02-app/01-building-your-application/04-caching/index.mdx b/docs/02-app/01-building-your-application/04-caching/index.mdx index 3a2dcc95ace14..9fc363c2a6105 100644 --- a/docs/02-app/01-building-your-application/04-caching/index.mdx +++ b/docs/02-app/01-building-your-application/04-caching/index.mdx @@ -306,7 +306,7 @@ There are two ways you can invalidate the Full Route Cache: You can opt out of the Full Route Cache, or in other words, dynamically render components for every incoming request, by: -- **Using a [Dynamic Function](#dynamic-functions)**: This will opt the route out from the Full Route Cache and dynamically render it at request time. The Data Cache can still be used. +- **Using a [Dynamic API](#dynamic-apis)**: This will opt the route out from the Full Route Cache and dynamically render it at request time. The Data Cache can still be used. - **Using the `dynamic = 'force-dynamic'` or `revalidate = 0` route segment config options**: This will skip the Full Route Cache and the Data Cache. Meaning components will be rendered and data fetched on every incoming request to the server. The Router Cache will still apply as it's a client-side cache. - **Opting out of the [Data Cache](#data-cache)**: If a route has a `fetch` request that is not cached, this will opt the route out of the Full Route Cache. The data for the specific `fetch` request will be fetched for every incoming request. Other `fetch` requests that do not opt out of caching will still be cached in the Data Cache. This allows for a hybrid of cached and uncached data. @@ -372,24 +372,24 @@ When configuring the different caching mechanisms, it's important to understand The following table provides an overview of how different Next.js APIs affect caching: -| API | Router Cache | Full Route Cache | Data Cache | React Request Memoization | -| ----------------------------------------------------------------------- | -------------------------- | --------------------- | --------------------- | ------------------------- | -| [``](#link) | Cache | | | | -| [`router.prefetch`](#routerprefetch) | Cache | | | | -| [`router.refresh`](#routerrefresh) | Revalidate | | | | -| [`fetch`](#fetch) | | | Cache | Cache | -| [`fetch` `options.cache`](#fetch-optionscache) | | | Cache or Opt out | | -| [`fetch` `options.next.revalidate`](#fetch-optionsnextrevalidate) | | Revalidate | Revalidate | | -| [`fetch` `options.next.tags`](#fetch-optionsnexttags-and-revalidatetag) | | Cache | Cache | | -| [`revalidateTag`](#fetch-optionsnexttags-and-revalidatetag) | Revalidate (Server Action) | Revalidate | Revalidate | | -| [`revalidatePath`](#revalidatepath) | Revalidate (Server Action) | Revalidate | Revalidate | | -| [`const revalidate`](#segment-config-options) | | Revalidate or Opt out | Revalidate or Opt out | | -| [`const dynamic`](#segment-config-options) | | Cache or Opt out | Cache or Opt out | | -| [`cookies`](#cookies) | Revalidate (Server Action) | Opt out | | | -| [`headers`, `searchParams`](#dynamic-functions) | | Opt out | | | -| [`generateStaticParams`](#generatestaticparams) | | Cache | | | -| [`React.cache`](#react-cache-function) | | | | Cache | -| [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) | | | Cache | | +| API | Router Cache | Full Route Cache | Data Cache | React Cache | +| ----------------------------------------------------------------------- | -------------------------- | --------------------- | --------------------- | ----------- | +| [``](#link) | Cache | | | | +| [`router.prefetch`](#routerprefetch) | Cache | | | | +| [`router.refresh`](#routerrefresh) | Revalidate | | | | +| [`fetch`](#fetch) | | | Cache | Cache | +| [`fetch` `options.cache`](#fetch-optionscache) | | | Cache or Opt out | | +| [`fetch` `options.next.revalidate`](#fetch-optionsnextrevalidate) | | Revalidate | Revalidate | | +| [`fetch` `options.next.tags`](#fetch-optionsnexttags-and-revalidatetag) | | Cache | Cache | | +| [`revalidateTag`](#fetch-optionsnexttags-and-revalidatetag) | Revalidate (Server Action) | Revalidate | Revalidate | | +| [`revalidatePath`](#revalidatepath) | Revalidate (Server Action) | Revalidate | Revalidate | | +| [`const revalidate`](#segment-config-options) | | Revalidate or Opt out | Revalidate or Opt out | | +| [`const dynamic`](#segment-config-options) | | Cache or Opt out | Cache or Opt out | | +| [`cookies`](#cookies) | Revalidate (Server Action) | Opt out | | | +| [`headers`, `searchParams`](#dynamic-apis) | | Opt out | | | +| [`generateStaticParams`](#generatestaticparams) | | Cache | | | +| [`React.cache`](#react-cache-function) | | | | Cache | +| [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) | | | Cache | | ### `` @@ -494,9 +494,9 @@ See the [`revalidatePath` API reference](/docs/app/api-reference/functions/reval > > The difference is that `revalidatePath` purges the Data Cache and Full Route Cache, whereas `router.refresh()` does not change the Data Cache and Full Route Cache, as it is a client-side API. -### Dynamic Functions +### Dynamic APIs -Dynamic functions like `cookies` and `headers`, and the `searchParams` prop in Pages depend on runtime incoming request information. Using them will opt a route out of the Full Route Cache, in other words, the route will be dynamically rendered. +Dynamic APIs like `cookies` and `headers`, and the `searchParams` prop in Pages depend on runtime incoming request information. Using them will opt a route out of the Full Route Cache, in other words, the route will be dynamically rendered. #### `cookies` diff --git a/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx b/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx index 8e447b2137d06..414db77be54b4 100644 --- a/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx +++ b/docs/02-app/01-building-your-application/06-optimizing/04-metadata.mdx @@ -52,8 +52,8 @@ You can use `generateMetadata` function to `fetch` metadata that requires dynami import type { Metadata, ResolvingMetadata } from 'next' type Props = { - params: { id: string } - searchParams: { [key: string]: string | string[] | undefined } + params: Promise<{ id: string }> + searchParams: Promise<{ [key: string]: string | string[] | undefined }> } export async function generateMetadata( @@ -61,7 +61,7 @@ export async function generateMetadata( parent: ResolvingMetadata ): Promise { // read route params - const id = params.id + const id = (await params).id // fetch data const product = await fetch(`https://.../${id}`).then((res) => res.json()) @@ -83,7 +83,7 @@ export default function Page({ params, searchParams }: Props) {} ```jsx filename="app/products/[id]/page.js" switcher export async function generateMetadata({ params, searchParams }, parent) { // read route params - const id = params.id + const id = (await params).id // fetch data const product = await fetch(`https://.../${id}`).then((res) => res.json()) diff --git a/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx b/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx index a83a2739f34e4..a2e92ed34defc 100644 --- a/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx +++ b/docs/02-app/01-building-your-application/06-optimizing/09-instrumentation.mdx @@ -5,7 +5,6 @@ related: title: Learn more about Instrumentation links: - app/api-reference/file-conventions/instrumentation - - app/api-reference/next-config-js/instrumentationHook --- {/* The content of this doc is shared between the app and pages router. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} @@ -40,7 +39,6 @@ See the [Next.js with OpenTelemetry example](https://github.com/vercel/next.js/t > **Good to know**: > -> - This feature is **experimental**. To use it, you must explicitly opt in by defining [`experimental.instrumentationHook = true;`](/docs/app/api-reference/next-config-js/instrumentationHook) in your `next.config.js`. > - The `instrumentation` file should be in the root of your project and not inside the `app` or `pages` directory. If you're using the `src` folder, then place the file inside `src` alongside `pages` and `app`. > - If you use the [`pageExtensions` config option](/docs/app/api-reference/next-config-js/pageExtensions) to add a suffix, you will also need to update the `instrumentation` filename to match. diff --git a/docs/02-app/01-building-your-application/06-optimizing/10-open-telemetry.mdx b/docs/02-app/01-building-your-application/06-optimizing/10-open-telemetry.mdx index 553e5b2ed95c9..270105d3a87a0 100644 --- a/docs/02-app/01-building-your-application/06-optimizing/10-open-telemetry.mdx +++ b/docs/02-app/01-building-your-application/06-optimizing/10-open-telemetry.mdx @@ -5,8 +5,6 @@ description: Learn how to instrument your Next.js app with OpenTelemetry. {/* The content of this doc is shared between the app and pages router. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} -> **Good to know**: This feature is **experimental**, you need to explicitly opt-in by providing `experimental.instrumentationHook = true;` in your `next.config.js`. - Observability is crucial for understanding and optimizing the behavior and performance of your Next.js app. As applications become more complex, it becomes increasingly difficult to identify and diagnose issues that may arise. By leveraging observability tools, such as logging and metrics, developers can gain insights into their application's behavior and identify areas for optimization. With observability, developers can proactively address issues before they become major problems and provide a better user experience. Therefore, it is highly recommended to use observability in your Next.js applications to improve performance, optimize resources, and enhance user experience. diff --git a/docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx b/docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx index b4c468f7c70af..6c41834932b85 100644 --- a/docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx +++ b/docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx @@ -189,14 +189,35 @@ Next.js can support both build time and runtime environment variables. **By default, environment variables are only available on the server**. To expose an environment variable to the browser, it must be prefixed with `NEXT_PUBLIC_`. However, these public environment variables will be inlined into the JavaScript bundle during `next build`. -To read runtime environment variables, we recommend using `getServerSideProps` or [incrementally adopting the App Router](/docs/app/building-your-application/upgrading/app-router-migration). With the App Router, we can safely read environment variables on the server during dynamic rendering. This allows you to use a singular Docker image that can be promoted through multiple environments with different values. + + +To read runtime environment variables, we recommend using `getServerSideProps` or [incrementally adopting the App Router](/docs/app/building-your-application/upgrading/app-router-migration). + + + + + +You can safely read environment variables on the server during dynamic rendering: + +```tsx filename="app/page.ts" switcher +import { connection } from 'next/server' + +export default async function Component() { + await connection() + // cookies, headers, and other Dynamic APIs + // will also opt into dynamic rendering, meaning + // this env variable is evaluated at runtime + const value = process.env.MY_VALUE + // ... +} +``` -```jsx -import { unstable_noStore as noStore } from 'next/cache' +```jsx filename="app/page.js" switcher +import { connection } from 'next/server' -export default function Component() { - noStore() - // cookies(), headers(), and other dynamic functions +export default async function Component() { + await connection() + // cookies, headers, and other Dynamic APIs // will also opt into dynamic rendering, meaning // this env variable is evaluated at runtime const value = process.env.MY_VALUE @@ -204,6 +225,10 @@ export default function Component() { } ``` + + +This allows you to use a singular Docker image that can be promoted through multiple environments with different values. + **Good to know:** - You can run code on server startup using the [`register` function](/docs/app/building-your-application/optimizing/instrumentation). diff --git a/docs/02-app/01-building-your-application/07-configuring/11-draft-mode.mdx b/docs/02-app/01-building-your-application/07-configuring/11-draft-mode.mdx index 7d671435628a1..908f1bca3bfc4 100644 --- a/docs/02-app/01-building-your-application/07-configuring/11-draft-mode.mdx +++ b/docs/02-app/01-building-your-application/07-configuring/11-draft-mode.mdx @@ -34,7 +34,8 @@ Then, import the [`draftMode`](/docs/app/api-reference/functions/draft-mode) fun import { draftMode } from 'next/headers' export async function GET(request: Request) { - draftMode().enable() + const draft = await draftMode() + draft.enable() return new Response('Draft mode is enabled') } ``` @@ -43,7 +44,8 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - draftMode().enable() + const draft = await draftMode() + draft.enable() return new Response('Draft mode is enabled') } ``` @@ -99,7 +101,8 @@ export async function GET(request: Request) { } // Enable Draft Mode by setting the cookie - draftMode().enable() + const draft = await draftMode() + draft.enable() // Redirect to the path from the fetched post // We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities @@ -133,7 +136,8 @@ export async function GET(request) { } // Enable Draft Mode by setting the cookie - draftMode().enable() + const draft = await draftMode() + draft.enable() // Redirect to the path from the fetched post // We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities @@ -156,7 +160,7 @@ Furthermore, the value of `isEnabled` will be `true`. import { draftMode } from 'next/headers' async function getData() { - const { isEnabled } = draftMode() + const { isEnabled } = await draftMode() const url = isEnabled ? 'https://draft.example.com' @@ -184,7 +188,7 @@ export default async function Page() { import { draftMode } from 'next/headers' async function getData() { - const { isEnabled } = draftMode() + const { isEnabled } = await draftMode() const url = isEnabled ? 'https://draft.example.com' diff --git a/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx b/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx index 49efab01e0df1..84ba41ab82f70 100644 --- a/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx +++ b/docs/02-app/01-building-your-application/07-configuring/15-content-security-policy.mdx @@ -177,8 +177,8 @@ You can now read the nonce from a [Server Component](/docs/app/building-your-app import { headers } from 'next/headers' import Script from 'next/script' -export default function Page() { - const nonce = headers().get('x-nonce') +export default async function Page() { + const nonce = (await headers()).get('x-nonce') return (