From 704e720c35e266d6b5c12b638662b298ed1d664d Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Thu, 12 Sep 2024 15:51:15 +0100 Subject: [PATCH 01/58] Mark unstable_after as stable, rename function mentions --- .../03-rendering/01-server-components.mdx | 2 +- .../02-api-reference/04-functions/after.mdx | 70 +++++++++++++++++ .../04-functions/unstable_after.mdx | 77 ------------------- 3 files changed, 71 insertions(+), 78 deletions(-) create mode 100644 docs/02-app/02-api-reference/04-functions/after.mdx delete mode 100644 docs/02-app/02-api-reference/04-functions/unstable_after.mdx 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..6af7be687dab8 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 @@ -106,7 +106,7 @@ Dynamic functions rely on information that can only be known at request time suc - [`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): +- [`after()`](/docs/app/api-reference/functions/after): - [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional) Using any of these functions will opt the whole route into dynamic rendering at request time. diff --git a/docs/02-app/02-api-reference/04-functions/after.mdx b/docs/02-app/02-api-reference/04-functions/after.mdx new file mode 100644 index 0000000000000..8de314133fb4d --- /dev/null +++ b/docs/02-app/02-api-reference/04-functions/after.mdx @@ -0,0 +1,70 @@ +--- +title: after +description: API Reference for the after function. +version: RC +--- + +`after()` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. + +It can be used in [Server Components](/docs/app/building-your-application/rendering/server-components) (including [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata)), [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Middleware](/docs/app/building-your-application/routing/middleware). + +The function accepts a callback that will be executed after the response is finished: + +```tsx filename="app/layout.tsx switcher +import { after } from 'next/server' +import { log } from '@/app/utils' + +export default function Layout({ children }: { children: React.ReactNode }) { + after(() => { + // Execute after the layout is rendered and sent to the user + log() + }) + return <>{children} +} +``` + +```jsx filename="app/layout.js switcher +import { after } from 'next/server' +import { log } from '@/app/utils' + +export default function Layout({ children }) { + after(() => { + // Execute after the layout is rendered and sent to the user + log() + }) + return <>{children} +} +``` + +> **Good to know**: +> +> - `after()` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound()` or `redirect()` is called. +> - `after()` is a [dynamic function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. +> - You can use React `cache` to deduplicate functions called inside `after()`. +> - [`cookies()`](/docs/app/api-reference/functions/cookies) cannot be set inside `after()` since the response has already been sent. +> - `after()` can be nested inside other `after()` calls. + +## Parameters + +- A callback function which will be executed after the response is finished. + +## Returns + +- `after()` does not return a value. + +## Alternatives + +The use case for `after()` is to process secondary tasks without blocking the primary response. It's similar to using the platform's [`waitUntil()`](https://vercel.com/docs/functions/functions-api-reference) or removing `await` from a promise, but with the following differences: + +- **`waitUntil()`**: accepts a promise and enqueues a task to be executed during the lifecycle of the request, whereas `after()` accepts a callback that will be executed **after** the response is finished. +- **Removing `await`**: starts executing during the response, which uses resources. It's also not reliable in serverless environments as the function stops computation immediately after the response is sent, potentially interrupting the task. + +We recommend using `after()` as it has been designed to consider other Next.js APIs and contexts. + +## Serverless function duration + +`after()` will run for the platform's default or configured max duration of a serverless function. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config#maxduration) route segment config. + +| Version History | Description | +| --------------- | --------------------------------------------------------------------- | +| `v15.0.0-rc` | `unstable_after` introduced, renamed to `after` and marked as stable. | diff --git a/docs/02-app/02-api-reference/04-functions/unstable_after.mdx b/docs/02-app/02-api-reference/04-functions/unstable_after.mdx deleted file mode 100644 index f4b13c3dc5bb0..0000000000000 --- a/docs/02-app/02-api-reference/04-functions/unstable_after.mdx +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: unstable_after -description: API Reference for the unstable_after function. -version: RC ---- - -`unstable_after()` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. - -It can be used in [Server Components](/docs/app/building-your-application/rendering/server-components) (including [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata)), [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Middleware](/docs/app/building-your-application/routing/middleware). - -To use `unstable_after()`, you need to enable it using the `experimental.after` config in the `next.config.js` file: - -```js filename="next.config.js" -const nextConfig = { - experimental: { - after: true, - }, -} -module.exports = nextConfig -``` - -The function accepts a callback that will be executed after the response is finished: - -```tsx filename="app/layout.tsx switcher -import { unstable_after as after } from 'next/server' -import { log } from '@/app/utils' - -export default function Layout({ children }: { children: React.ReactNode }) { - after(() => { - // Execute after the layout is rendered and sent to the user - log() - }) - return <>{children} -} -``` - -```jsx filename="app/layout.js switcher -import { unstable_after as after } from 'next/server' -import { log } from '@/app/utils' - -export default function Layout({ children }) { - after(() => { - // Execute after the layout is rendered and sent to the user - log() - }) - return <>{children} -} -``` - -> **Good to know**: -> -> - `unstable_after()` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound()` or `redirect()` is called. -> - `unstable_after()` is a [dynamic function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. -> - You can use React `cache` to deduplicate functions called inside `unstable_after()`. -> - [`cookies()`](/docs/app/api-reference/functions/cookies) cannot be set inside `unstable_after()` since the response has already been sent. -> - `unstable_after()` can be nested inside other `unstable_after()` calls. - -## Parameters - -- A callback function which will be executed after the response is finished. - -## Returns - -- `unstable_after()` does not return a value. - -## Alternatives - -The use case for `unstable_after()` is to process secondary tasks without blocking the primary response. It's similar to using the platform's [`waitUntil()`](https://vercel.com/docs/functions/functions-api-reference) or removing `await` from a promise, but with the following differences: - -- **`waitUntil()`**: accepts a promise and enqueues a task to be executed during the lifecycle of the request, whereas `unstable_after()` accepts a callback that will be executed **after** the response is finished. -- **Removing `await`**: starts executing during the response, which uses resources. It's also not reliable in serverless environments as the function stops computation immediately after the response is sent, potentially interrupting the task. - -We recommend using `unstable_after()` as it has been designed to consider other Next.js APIs and contexts. - -## Serverless function duration - -`unstable_after()` will run for the platform's default or configured max duration of a serverless function. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config#maxduration) route segment config. From 1672a6eba4e854e35fd6169be00eacb2b3c8e136 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Thu, 12 Sep 2024 16:20:58 +0100 Subject: [PATCH 02/58] Mark intrumentation as stable, remove instrumentationHook --- .../02-file-conventions/instrumentation.mdx | 37 +++---------------- .../05-next-config-js/instrumentationHook.mdx | 28 -------------- .../04-next-config-js/instrumentationHook.mdx | 7 ---- 3 files changed, 5 insertions(+), 67 deletions(-) delete mode 100644 docs/02-app/02-api-reference/05-next-config-js/instrumentationHook.mdx delete mode 100644 docs/03-pages/02-api-reference/04-next-config-js/instrumentationHook.mdx diff --git a/docs/02-app/02-api-reference/02-file-conventions/instrumentation.mdx b/docs/02-app/02-api-reference/02-file-conventions/instrumentation.mdx index b77adac4325f2..73a5659152f6a 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/instrumentation.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/instrumentation.mdx @@ -11,33 +11,6 @@ The `instrumentation.js|ts` file is used to integrate observability tools into y To use it, place the file in the **root** of your application or inside a [`src` folder](/docs/app/building-your-application/configuring/src-directory) if using one. -## Enabling Instrumentation - -Instrumentation is currently an experimental feature, to use the `instrumentation.js` file, you must explicitly opt-in by defining [`experimental.instrumentationHook = true;`](/docs/app/api-reference/next-config-js/instrumentationHook) in your `next.config.js`: - -```ts filename="next.config.ts" switcher -import type { NextConfig } from 'next' - -const nextConfig: NextConfig = { - experimental: { - instrumentationHook: true, - }, -} - -export default nextConfig -``` - -```js filename="next.config.js" switcher -/** @type {import('next').NextConfig} */ -const nextConfig = { - experimental: { - instrumentationHook: true, - }, -} - -module.exports = nextConfig -``` - ## Exports ### `register` (optional) @@ -161,8 +134,8 @@ export function onRequestError() { ## Version History -| Version | Changes | -| --------- | ------------------------------------------------------- | -| `v15.0.0` | `onRequestError` introduced | -| `v14.0.4` | Turbopack support for `instrumentation` | -| `v13.2.0` | `instrumentation` introduced as an experimental feature | +| Version | Changes | +| ------------ | ------------------------------------------------------- | +| `v15.0.0-RC` | `onRequestError` introduced, `instrumentation` stable | +| `v14.0.4` | Turbopack support for `instrumentation` | +| `v13.2.0` | `instrumentation` introduced as an experimental feature | diff --git a/docs/02-app/02-api-reference/05-next-config-js/instrumentationHook.mdx b/docs/02-app/02-api-reference/05-next-config-js/instrumentationHook.mdx deleted file mode 100644 index 83ff75c02068e..0000000000000 --- a/docs/02-app/02-api-reference/05-next-config-js/instrumentationHook.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: instrumentationHook -description: Use the instrumentationHook option to set up instrumentation in your Next.js App. -related: - title: Learn more about Instrumentation - links: - - app/api-reference/file-conventions/instrumentation - - app/building-your-application/optimizing/instrumentation -version: experimental ---- - -{/* 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. */} - -{/* TODO: Add note about it not being needed once version 15 is stable */} - -The experimental `instrumentationHook` option allows you to set up instrumentation via the [`instrumentation` file](/docs/app/api-reference/file-conventions/instrumentation) in your Next.js App. - -```js filename="next.config.js" -module.exports = { - experimental: { - instrumentationHook: true, - }, -} -``` - -| Version | Changes | -| --------- | ----------------------------------------------------------- | -| `v13.2.0` | `instrumentationHook` introduced as an experimental feature | diff --git a/docs/03-pages/02-api-reference/04-next-config-js/instrumentationHook.mdx b/docs/03-pages/02-api-reference/04-next-config-js/instrumentationHook.mdx deleted file mode 100644 index 784b768d37634..0000000000000 --- a/docs/03-pages/02-api-reference/04-next-config-js/instrumentationHook.mdx +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: instrumentationHook -description: Use the instrumentationHook option to set up instrumentation in your Next.js App. -source: 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. */} From a0791b3f51639bdd3b270dd2bcae0b2c412453ad Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Thu, 12 Sep 2024 16:24:59 +0100 Subject: [PATCH 03/58] Remove instrumentation experimental notes --- .../06-optimizing/09-instrumentation.mdx | 2 -- .../06-optimizing/10-open-telemetry.mdx | 2 -- 2 files changed, 4 deletions(-) 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 b5aa71b7c9aa5..a120641aad88a 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. From b8106005e91772ca19a0ae8dfa07750425a73280 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Thu, 12 Sep 2024 16:58:07 +0100 Subject: [PATCH 04/58] Improve turbo docs, add missing options --- .../05-next-config-js/turbo.mdx | 91 ++++++++++++++----- 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx index 074d31b9959dc..701e20f02bf40 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx @@ -6,14 +6,70 @@ version: experimental {/* 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. */} -Turbopack can be customized to transform different files and change how modules are resolved. +The `turbo` option allows you customize [Turbopack](/docs/architecture/turbopack) to transform different files and change how modules are resolved. + +```ts filename="next.config.ts" switcher +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + experimental: { + turbo: { + // ... + }, + }, +} + +export default nextConfig +``` + +```js filename="next.config.js" switcher +/** @type {import('next').NextConfig} */ +const nextConfig = { + experimental: { + turbo: { + // ... + }, + }, +} + +module.exports = nextConfig +``` > **Good to know**: > -> - These features are experimental and will only work with `next --turbo`. > - Turbopack for Next.js does not require loaders nor loader configuration for built-in functionality. Turbopack has built-in support for css and compiling modern JavaScript, so there's no need for `css-loader`, `postcss-loader`, or `babel-loader` if you're using `@babel/preset-env`. -## webpack loaders +## Reference + +### Options + +The following options are available for the `turbo` configuration: + +| Option | Description | +| ------------------- | ------------------------------------------------------------------------- | +| `rules` | List of unsupported webpack loaders to apply when running with Turbopack. | +| `resolveAlias` | Map aliased imports to modules to load in their place. | +| `resolveExtensions` | List of extensions to resolve when importing files. | +| `moduleIdStrategy` | Assign module IDs | +| `useSwcCss` | Use `swc_css` instead of `lightningcss` for Turbopack | +| `treeshaking` | Enable tree shaking for the turbopack dev server and build. | +| `memoryLimit` | A target memory limit for turbo, in bytes. | + +### Supported loaders + +The following loaders have been tested to work with Turbopack's webpack loader implementation: + +- [`babel-loader`](https://www.npmjs.com/package/babel-loader) +- [`@svgr/webpack`](https://www.npmjs.com/package/@svgr/webpack) +- [`svg-inline-loader`](https://www.npmjs.com/package/svg-inline-loader) +- [`yaml-loader`](https://www.npmjs.com/package/yaml-loader) +- [`string-replace-loader`](https://www.npmjs.com/package/string-replace-loader) +- [`raw-loader`](https://www.npmjs.com/package/raw-loader) +- [`sass-loader`](https://www.npmjs.com/package/sass-loader) + +## Examples + +### Configuring webpack loaders If you need loader support beyond what's built in, many webpack loaders already work with Turbopack. There are currently some limitations: @@ -40,21 +96,9 @@ module.exports = { > **Good to know**: Prior to Next.js version 13.4.4, `experimental.turbo.rules` was named `experimental.turbo.loaders` and only accepted file extensions like `.mdx` instead of `*.mdx`. -### Supported loaders +### Resolving aliases -The following loaders have been tested to work with Turbopack's webpack loader implementation: - -- [`babel-loader`](https://www.npmjs.com/package/babel-loader) -- [`@svgr/webpack`](https://www.npmjs.com/package/@svgr/webpack) -- [`svg-inline-loader`](https://www.npmjs.com/package/svg-inline-loader) -- [`yaml-loader`](https://www.npmjs.com/package/yaml-loader) -- [`string-replace-loader`](https://www.npmjs.com/package/string-replace-loader) -- [`raw-loader`](https://www.npmjs.com/package/raw-loader) -- [`sass-loader`](https://www.npmjs.com/package/sass-loader) - -## Resolve aliases - -Through `next.config.js`, Turbopack can be configured to modify module resolution through aliases, similar to webpack's [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) configuration. +Turbopack can be configured to modify module resolution through aliases, similar to webpack's [`resolve.alias`](https://webpack.js.org/configuration/resolve/#resolvealias) configuration. To configure resolve aliases, map imported patterns to their new destination in `next.config.js`: @@ -75,9 +119,9 @@ This aliases imports of the `underscore` package to the `lodash` package. In oth Turbopack also supports conditional aliasing through this field, similar to Node.js's [conditional exports](https://nodejs.org/docs/latest-v18.x/api/packages.html#conditional-exports). At the moment only the `browser` condition is supported. In the case above, imports of the `mocha` module will be aliased to `mocha/browser-entry.js` when Turbopack targets browser environments. -## Resolve extensions +### Resolving custom extensions -Through `next.config.js`, Turbopack can be configured to resolve modules with custom extensions, similar to webpack's [`resolve.extensions`](https://webpack.js.org/configuration/resolve/#resolveextensions) configuration. +Turbopack can be configured to resolve modules with custom extensions, similar to webpack's [`resolve.extensions`](https://webpack.js.org/configuration/resolve/#resolveextensions) configuration. To configure resolve extensions, use the `resolveExtensions` field in `next.config.js`: @@ -103,13 +147,12 @@ This overwrites the original resolve extensions with the provided list. Make sur For more information and guidance for how to migrate your app to Turbopack from webpack, see [Turbopack's documentation on webpack compatibility](https://turbo.build/pack/docs/migrating-from-webpack). -## Module ID strategy - -Turbopack currently supports two strategies for assigning module IDs: `'named'` and `'deterministic'`. +### Assigning module IDs -`'named'` assigns readable module IDs based on the module's path and functionality. +Turbopack currently supports two strategies for assigning module IDs: -`'deterministic'` assigns small hashed numeric module IDs, which are mostly consistent between builds and therefore help with long-term caching. +- `'named'` assigns readable module IDs based on the module's path and functionality. +- `'deterministic'` assigns small hashed numeric module IDs, which are mostly consistent between builds and therefore help with long-term caching. If not set, Turbopack will use `'named'` for development builds and `'deterministic'` for production builds. From 3b6457ede9bcb8f9f4cdf8d65cdf9999220d0d11 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 10:03:18 +0100 Subject: [PATCH 05/58] Document `connection()` --- .../04-functions/connection.mdx | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 docs/02-app/02-api-reference/04-functions/connection.mdx diff --git a/docs/02-app/02-api-reference/04-functions/connection.mdx b/docs/02-app/02-api-reference/04-functions/connection.mdx new file mode 100644 index 0000000000000..5312b5e6cca6c --- /dev/null +++ b/docs/02-app/02-api-reference/04-functions/connection.mdx @@ -0,0 +1,58 @@ +--- +title: connection +description: API Reference for the connection function. +version: RC +--- + +The `connection()` function allows you to indicate rendering should wait for an incoming user request before continuing. + +It's useful when a component doesn’t use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-functions), but you want it to be dynamically rendered at runtime and not statically rendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `Date.now()`. + +```ts filename="app/page.tsx" switcher +import { connection } from 'next/server' + +export default async function Page() { + await connection() + // Everything below will be excluded from prerendering + const rand = Math.random() + return {rand} +} +``` + +```jsx filename="app/page.js" switcher +import { connection } from 'next/server' + +export default async function Page() { + await connection() + // Everything below will be excluded from prerendering + const rand = Math.random() + return {rand} +} +``` + +## Reference + +### Type + +```jsx +function connection(): Promise +``` + +### Parameters + +- The function does not accept any parameters. + +### Returns + +- The function returns a `void` Promise. It is not meant to be consumed. + +## Good to know + +- `connection` replaces [`unstable_noStore`](/docs/app/api-reference/functions/unstable_noStore) to better align with the future of Next.js. +- The function is only necessary when dynamic rendering is required and common Dynamic APIs are not used. + +### Version History + +| Version | Changes | +| ------------ | ------------------------ | +| `v15.0.0-RC` | `connection` introduced. | From 69ce0b1f1db028b66e9ad1f3aed1a0e4a6f7b5ef Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 10:05:42 +0100 Subject: [PATCH 06/58] Tweak --- docs/02-app/02-api-reference/04-functions/connection.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/02-api-reference/04-functions/connection.mdx b/docs/02-app/02-api-reference/04-functions/connection.mdx index 5312b5e6cca6c..5567bf34ab206 100644 --- a/docs/02-app/02-api-reference/04-functions/connection.mdx +++ b/docs/02-app/02-api-reference/04-functions/connection.mdx @@ -6,7 +6,7 @@ version: RC The `connection()` function allows you to indicate rendering should wait for an incoming user request before continuing. -It's useful when a component doesn’t use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-functions), but you want it to be dynamically rendered at runtime and not statically rendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `Date.now()`. +It's useful when a component doesn’t use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-functions), but you want it to be dynamically rendered at runtime and not statically rendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `new Date()`. ```ts filename="app/page.tsx" switcher import { connection } from 'next/server' From 8118eb843fc842ce7d3e450fa1f541d26fec15c4 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 10:08:41 +0100 Subject: [PATCH 07/58] Add note to unstable_noStore about deprecation --- docs/02-app/02-api-reference/04-functions/unstable_noStore.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/02-app/02-api-reference/04-functions/unstable_noStore.mdx b/docs/02-app/02-api-reference/04-functions/unstable_noStore.mdx index aa289da672e5b..9403baeb5a851 100644 --- a/docs/02-app/02-api-reference/04-functions/unstable_noStore.mdx +++ b/docs/02-app/02-api-reference/04-functions/unstable_noStore.mdx @@ -4,6 +4,8 @@ description: API Reference for the unstable_noStore function. version: unstable --- +> This API will be deprecated in the future. In version 15, we recommend using [`connection`](/docs/app/api-reference/functions/connection) instead of `unstable_noStore`. + `unstable_noStore` can be used to declaratively opt out of static rendering and indicate a particular component should not be cached. ```jsx From ef577a88353c7bd2841d594e22a1912067fd0916 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 11:00:41 +0100 Subject: [PATCH 08/58] Update cookies API reference --- .../02-api-reference/04-functions/cookies.mdx | 113 +++++++++++------- 1 file changed, 71 insertions(+), 42 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index 7b2b78bda4196..32da81138f6b7 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -3,13 +3,13 @@ title: cookies description: API Reference for the cookies function. --- -The `cookies` function allows you to read the HTTP incoming request cookies from a [Server Component](/docs/app/building-your-application/rendering/server-components) or write outgoing request cookies in a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). +`cookies` is an **async** function that allows you to read the HTTP incoming request cookies in components, and read/write outgoing request cookies in a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). ```tsx filename="app/page.tsx" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() const theme = cookieStore.get('theme') return '...' } @@ -18,8 +18,8 @@ export default function Page() { ```js filename="app/page.js" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() const theme = cookieStore.get('theme') return '...' } @@ -62,8 +62,10 @@ When setting a cookie, the following properties from the `options` object are su To learn more about these options, see the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies). -## Caveats +## Good to know +- `cookies()` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access cookies. + - In version 14 and earlier, `cookies()` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. - `cookies()` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). - The `.delete()` method can only be called: - In a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). @@ -79,8 +81,8 @@ You can use the `cookies().get('name')` method to get a single cookie: ```tsx filename="app/page.tsx" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() const theme = cookieStore.get('theme') return '...' } @@ -89,8 +91,8 @@ export default function Page() { ```jsx filename="app/page.js" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() const theme = cookieStore.get('theme') return '...' } @@ -103,8 +105,8 @@ You can use the `cookies().getAll()` method to get all cookies with a matching n ```tsx filename="app/page.tsx" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() return cookieStore.getAll().map((cookie) => (

Name: {cookie.name}

@@ -117,8 +119,8 @@ export default function Page() { ```jsx filename="app/page.js" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() return cookieStore.getAll().map((cookie) => (

Name: {cookie.name}

@@ -137,12 +139,12 @@ You can use the `cookies().set(name, value, options)` method in a [Server Action import { cookies } from 'next/headers' -async function create(data) { - cookies().set('name', 'lee') +export async function create(data) { + await cookies().set('name', 'lee') // or - cookies().set('name', 'lee', { secure: true }) + await cookies().set('name', 'lee', { secure: true }) // or - cookies().set({ + await cookies().set({ name: 'name', value: 'lee', httpOnly: true, @@ -156,12 +158,12 @@ async function create(data) { import { cookies } from 'next/headers' -async function create(data) { - cookies().set('name', 'lee') +export async function create(data) { + await cookies().set('name', 'lee') // or - cookies().set('name', 'lee', { secure: true }) + await cookies().set('name', 'lee', { secure: true }) // or - cookies().set({ + await cookies().set({ name: 'name', value: 'lee', httpOnly: true, @@ -170,15 +172,15 @@ async function create(data) { } ``` -### Check if a cookie exists +### Checking if a cookie exists You can use the `cookies().has(name)` method to check if a cookie exists: ```tsx filename="app/page.ts" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() const hasCookie = cookieStore.has('theme') return '...' } @@ -187,8 +189,8 @@ export default function Page() { ```jsx filename="app/page.js" switcher import { cookies } from 'next/headers' -export default function Page() { - const cookieStore = cookies() +export default async function Page() { + const cookieStore = await cookies() const hasCookie = cookieStore.has('theme') return '...' } @@ -205,8 +207,8 @@ Using the `delete()` method: import { cookies } from 'next/headers' -async function delete(data) { - cookies().delete('name') +export async function delete(data) { + await cookies().delete('name') } ``` @@ -215,8 +217,8 @@ async function delete(data) { import { cookies } from 'next/headers' -async function delete(data) { - cookies().delete('name') +export async function delete(data) { + await cookies().delete('name') } ``` @@ -227,8 +229,8 @@ Setting a new cookie with the same name and an empty value: import { cookies } from 'next/headers' -async function delete(data) { - cookies().set('name', '') +export async function delete(data) { + await cookies().set('name', '') } ``` @@ -237,8 +239,8 @@ async function delete(data) { import { cookies } from 'next/headers' -async function delete(data) { - cookies().set('name', '') +export async function delete(data) { + await cookies().set('name', '') } ``` @@ -249,8 +251,8 @@ Setting the `maxAge` to 0 will immediately expire a cookie. `maxAge` accepts a v import { cookies } from 'next/headers' -async function delete(data) { - cookies().set('name', 'value', { maxAge: 0 }) +export async function delete(data) { + await cookies().set('name', 'value', { maxAge: 0 }) } ``` @@ -259,13 +261,40 @@ async function delete(data) { import { cookies } from 'next/headers' -async function delete(data) { - cookies().set('name', 'value', { maxAge: 0 }) +export async function delete(data) { + await cookies().set('name', 'value', { maxAge: 0 }) +} +``` + +### Cookies in synchronous components + +To use `cookies` in a synchronous component, such as a Client Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: + +```tsx filename="app/ui/component.tsx" switcher +'use client' + +import { cookies } from 'next/headers' +import { use } from 'react' + +export default async function Component() { + const theme = use(cookies()).get('theme') +} +``` + +```js filename="app/ui/component.js" switcher +'use client' + +import { cookies } from 'next/headers' +import { use } from 'react' + +export default async function Component() { + const theme = use(cookies()).get('theme') } ``` ## Version History -| Version | Changes | -| --------- | --------------------- | -| `v13.0.0` | `cookies` introduced. | +| Version | Changes | +| ------------ | ------------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `cookies` is now an async function. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available. | +| `v13.0.0` | `cookies` introduced. | From ba46bd8a7b8b095810fcbe59e8be9a407139b9d0 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 11:07:14 +0100 Subject: [PATCH 09/58] Correction --- docs/02-app/02-api-reference/04-functions/cookies.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index 32da81138f6b7..98e7f226284da 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -3,7 +3,7 @@ title: cookies description: API Reference for the cookies function. --- -`cookies` is an **async** function that allows you to read the HTTP incoming request cookies in components, and read/write outgoing request cookies in a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). +`cookies` is an **async** function that allows you to read the HTTP incoming request cookies in [Server Component](/docs/app/building-your-application/rendering/server-components), and read/write outgoing request cookies in [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handlers](/docs/app/building-your-application/routing/route-handlers). ```tsx filename="app/page.tsx" switcher import { cookies } from 'next/headers' @@ -268,7 +268,7 @@ export async function delete(data) { ### Cookies in synchronous components -To use `cookies` in a synchronous component, such as a Client Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: +To use `cookies` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: ```tsx filename="app/ui/component.tsx" switcher 'use client' From b1e21a74e87c9353827b3bc2d181c502df67eb98 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 11:26:46 +0100 Subject: [PATCH 10/58] Update headers API reference --- .../02-api-reference/04-functions/headers.mdx | 63 +++++++------------ 1 file changed, 22 insertions(+), 41 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index 75cea804350fd..885d6935e03bc 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -3,17 +3,13 @@ title: headers description: API reference for the headers function. --- -The `headers` function allows you to read the HTTP incoming request headers from a [Server Component](/docs/app/building-your-application/rendering/server-components). - -## `headers()` - -This API extends the [Web Headers API](https://developer.mozilla.org/docs/Web/API/Headers). It is **read-only**, meaning you cannot `set` / `delete` the outgoing request headers. +`headers` is an **async** function that allows you to **read** the HTTP incoming request headers from a [Server Component](/docs/app/building-your-application/rendering/server-components). ```tsx filename="app/page.tsx" switcher import { headers } from 'next/headers' -export default function Page() { - const headersList = headers() +export default async function Page() { + const headersList = await headers() const referer = headersList.get('referer') return
Referer: {referer}
@@ -23,29 +19,21 @@ export default function Page() { ```jsx filename="app/page.js" switcher import { headers } from 'next/headers' -export default function Page() { - const headersList = headers() +export default async function Page() { + const headersList = await headers() const referer = headersList.get('referer') return
Referer: {referer}
} ``` -> **Good to know**: -> -> - `headers()` is a **[Dynamic Function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions)** whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)** at request time. - -### API Reference +## Reference -```tsx -const headersList = headers() -``` - -#### Parameters +### Parameters `headers` does not take any parameters. -#### Returns +### Returns `headers` returns a **read-only** [Web Headers](https://developer.mozilla.org/docs/Web/API/Headers) object. @@ -56,18 +44,20 @@ const headersList = headers() - [`Headers.keys()`](https://developer.mozilla.org/docs/Web/API/Headers/keys): Returns an [`iterator`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols) allowing you to go through all keys of the key/value pairs contained in this object. - [`Headers.values()`](https://developer.mozilla.org/docs/Web/API/Headers/values): Returns an [`iterator`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Iteration_protocols) allowing you to go through all values of the key/value pairs contained in this object. -### Examples +## Good to know -#### Usage with Data Fetching +- Since `headers()` is read-only, you cannot `set` or `delete` the outgoing request headers. +- `headers()` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)**. -`headers()` can be used in combination with React `Suspense`: +## Examples -```jsx filename="app/page.js" -import { Suspense } from 'react' +### Using the Authorization header + +````jsx filename="app/page.js" import { headers } from 'next/headers' -async function User() { - const authorization = headers().get('authorization') +export default async function Page() { + const authorization = await headers().get('authorization') const res = await fetch('...', { headers: { authorization }, // Forward the authorization header }) @@ -75,16 +65,6 @@ async function User() { return

{user.name}

} - -export default function Page() { - return ( - - - - ) -} -``` - #### IP Address `headers()` can be used to get the IP address of the client. @@ -111,7 +91,7 @@ export default function Page() { ) } -``` +```` In addition to `x-forwarded-for`, `headers()` can also read: @@ -122,6 +102,7 @@ In addition to `x-forwarded-for`, `headers()` can also read: ## Version History -| Version | Changes | -| --------- | --------------------- | -| `v13.0.0` | `headers` introduced. | +| Version | Changes | +| ------------ | ------------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `headers` is now an async function. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available. | +| `v13.0.0` | `headers` introduced. | From 4122da17b948c528e5c2887b986a35d09ec2e8e7 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 11:28:03 +0100 Subject: [PATCH 11/58] Remove IP address example - See https://github.com/vercel/next.js/pull/68379 - Additionally, people can read the headers available in their system, we don't have to document them individually in the API for accessing them. --- .../02-api-reference/04-functions/headers.mdx | 37 +------------------ 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index 885d6935e03bc..f429ec947062d 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -53,7 +53,7 @@ export default async function Page() { ### Using the Authorization header -````jsx filename="app/page.js" +```jsx filename="app/page.js" import { headers } from 'next/headers' export default async function Page() { @@ -65,40 +65,7 @@ export default async function Page() { return

{user.name}

} -#### IP Address - -`headers()` can be used to get the IP address of the client. - -```jsx filename="app/page.js" -import { Suspense } from 'react' -import { headers } from 'next/headers' - -function IP() { - const FALLBACK_IP_ADDRESS = '0.0.0.0' - const forwardedFor = headers().get('x-forwarded-for') - - if (forwardedFor) { - return forwardedFor.split(',')[0] ?? FALLBACK_IP_ADDRESS - } - - return headers().get('x-real-ip') ?? FALLBACK_IP_ADDRESS -} - -export default function Page() { - return ( - - - - ) -} -```` - -In addition to `x-forwarded-for`, `headers()` can also read: - -- `x-real-ip` -- `x-forwarded-host` -- `x-forwarded-port` -- `x-forwarded-proto` +``` ## Version History From 8de187bc540c17587aeb57a232ad929821ab4f9a Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 13:31:53 +0100 Subject: [PATCH 12/58] Fix broken links --- docs/04-architecture/turbopack.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/04-architecture/turbopack.mdx b/docs/04-architecture/turbopack.mdx index 98b5c7bf6abb4..0de3606a52b66 100644 --- a/docs/04-architecture/turbopack.mdx +++ b/docs/04-architecture/turbopack.mdx @@ -40,11 +40,11 @@ These features are currently not supported: - [`webpack()`](/docs/app/api-reference/next-config-js/webpack) configuration in `next.config.js` - Turbopack replaces Webpack, this means that webpack configuration is not supported. - To configure Turbopack, [see the documentation](/docs/app/api-reference/next-config-js/turbo). - - A subset of [Webpack loaders](/docs/app/api-reference/next-config-js/turbo#webpack-loaders) are supported in Turbopack. + - A subset of [Webpack loaders](/docs/app/api-reference/next-config-js/turbo#configuring-webpack-loaders) are supported in Turbopack. - Babel (`.babelrc`) - Turbopack leverages the [SWC](/docs/architecture/nextjs-compiler#why-swc) compiler for all transpilation and optimizations. This means that Babel is not included by default. - If you have a `.babelrc` file, you might no longer need it because Next.js includes common Babel plugins as SWC transforms that can be enabled. You can read more about this in the [compiler documentation](docs/architecture/nextjs-compiler#supported-features). - - If you still need to use Babel after verifying your particular use case is not covered, you can leverage Turbopack's [support for custom webpack loaders](/docs/app/api-reference/next-config-js/turbo#webpack-loaders) to include `babel-loader`. + - If you still need to use Babel after verifying your particular use case is not covered, you can leverage Turbopack's [support for custom webpack loaders](/docs/app/api-reference/next-config-js/turbo#configuring-webpack-loaders) to include `babel-loader`. - Creating a root layout automatically in App Router. - This behavior is currently not supported since it changes input files, instead, an error will be shown for you to manually add a root layout in the desired location. - `@next/font` (legacy font support). From 220178e4b9ff7f972d57c78052f698bb7929b2d8 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 13:32:01 +0100 Subject: [PATCH 13/58] More corrections --- docs/02-app/02-api-reference/04-functions/cookies.mdx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index 98e7f226284da..c035496f48c7a 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -271,23 +271,19 @@ export async function delete(data) { To use `cookies` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: ```tsx filename="app/ui/component.tsx" switcher -'use client' - import { cookies } from 'next/headers' import { use } from 'react' -export default async function Component() { +export default function Component() { const theme = use(cookies()).get('theme') } ``` ```js filename="app/ui/component.js" switcher -'use client' - import { cookies } from 'next/headers' import { use } from 'react' -export default async function Component() { +export default function Component() { const theme = use(cookies()).get('theme') } ``` From b551afe8d7eb5984ab75a1ef2c718947f84cce5f Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 13:34:42 +0100 Subject: [PATCH 14/58] Add synchronous example for headers --- .../02-api-reference/04-functions/headers.mdx | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index f429ec947062d..cd08838dda745 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -46,6 +46,8 @@ export default async function Page() { ## Good to know +- `headers()` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. + - In version 14 and earlier, `headers()` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. - Since `headers()` is read-only, you cannot `set` or `delete` the outgoing request headers. - `headers()` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)**. @@ -67,6 +69,28 @@ export default async function Page() { } ``` +### Reading headers in synchronous components + +To use `cookies` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: + +```tsx filename="app/ui/component.tsx" switcher +import { headers } from 'next/headers' +import { use } from 'react' + +export function Component() { + const userAgent = use(headers().get('user-agent')) +} +``` + +```js filename="app/ui/component.js" switcher +import { cookies } from 'next/headers' +import { use } from 'react' + +export function Component() { + const userAgent = use(headers().get('user-agent')) +} +``` + ## Version History | Version | Changes | From 98feccbcb87cbf493bad29651d9f7bf173dbe196 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 13:36:06 +0100 Subject: [PATCH 15/58] Simplify headers example --- docs/02-app/02-api-reference/04-functions/headers.mdx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index cd08838dda745..b8285531525d8 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -10,9 +10,7 @@ import { headers } from 'next/headers' export default async function Page() { const headersList = await headers() - const referer = headersList.get('referer') - - return
Referer: {referer}
+ const userAgent = headersList.get('user-agent') } ``` @@ -21,9 +19,7 @@ import { headers } from 'next/headers' export default async function Page() { const headersList = await headers() - const referer = headersList.get('referer') - - return
Referer: {referer}
+ const userAgent = headersList.get('user-agent') } ``` From f49685aee9781771c57bbf385d70bcdfe5e37f71 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 13:38:47 +0100 Subject: [PATCH 16/58] Update draftMode API reference page --- .../04-functions/draft-mode.mdx | 73 +++++++++++++++---- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx index 4f5d0591a4038..ce0d5e672e210 100644 --- a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx +++ b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx @@ -8,7 +8,23 @@ related: - app/building-your-application/configuring/draft-mode --- -The `draftMode` function allows you to enable and disable [Draft Mode](/docs/app/building-your-application/configuring/draft-mode), as well as check if Draft Mode is enabled in a [Server Component](/docs/app/building-your-application/rendering/server-components). +`draftMode` is an **async** function allows you to enable and disable [Draft Mode](/docs/app/building-your-application/configuring/draft-mode), as well as check if Draft Mode is enabled in a [Server Component](/docs/app/building-your-application/rendering/server-components). + +```tsx filename="app/page.ts" +import { draftMode } from 'next/headers' + +export default async function Page() { + const { isEnabled } = await draftMode() +} +``` + +```jsx filename="app/page.js" +import { draftMode } from 'next/headers' + +export default async function Page() { + const { isEnabled } = await draftMode() +} +``` ## Reference @@ -20,10 +36,12 @@ The following methods and properties are available: | `enable()` | Enables Draft Mode in a Route Handler by setting a cookie (`__prerender_bypass`). | | `disable()` | Disables Draft Mode in a Route Handler by deleting a cookie. | -> **Good to know:** -> -> - A new bypass cookie value will be generated each time you run `next build`. This ensures that the bypass cookie can’t be guessed. -> - To test Draft Mode locally over HTTP, your browser will need to allow third-party cookies and local storage access. +## Good to know + +- `draftMode()` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. + - In version 14 and earlier, `draftMode()` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. +- A new bypass cookie value will be generated each time you run `next build`. This ensures that the bypass cookie can’t be guessed. +- To test Draft Mode locally over HTTP, your browser will need to allow third-party cookies and local storage access. ## Examples @@ -35,7 +53,7 @@ To enable Draft Mode, create a new [Route Handler](/docs/app/building-your-appli import { draftMode } from 'next/headers' export async function GET(request: Request) { - draftMode().enable() + await draftMode().enable() return new Response('Draft mode is enabled') } ``` @@ -44,7 +62,7 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - draftMode().enable() + await draftMode().enable() return new Response('Draft mode is enabled') } ``` @@ -59,7 +77,7 @@ To disable Draft Mode manually, call the `disable()` method in your [Route Handl import { draftMode } from 'next/headers' export async function GET(request: Request) { - draftMode().disable() + await draftMode().disable() return new Response('Draft mode is disabled') } ``` @@ -68,7 +86,7 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - draftMode().disable() + await draftMode().disable() return new Response('Draft mode is disabled') } ``` @@ -82,8 +100,8 @@ You can check if Draft Mode is enabled in a Server Component with the `isEnabled ```tsx filename="app/page.ts" import { draftMode } from 'next/headers' -export default function Page() { - const { isEnabled } = draftMode() +export default async function Page() { + const { isEnabled } = await draftMode() return (

My Blog Post

@@ -96,8 +114,8 @@ export default function Page() { ```jsx filename="app/page.js" import { draftMode } from 'next/headers' -export default function Page() { - const { isEnabled } = draftMode() +export default async function Page() { + const { isEnabled } = await draftMode() return (

My Blog Post

@@ -107,8 +125,31 @@ export default function Page() { } ``` +For synchronous components, you can use the React [`use`](https://react.dev/reference/react/use) function: + +```tsx filename="app/page.tsx" switcher +import { draftMode } from 'next/headers' + +export default function Page() { + const { isEnabled } = use(draftMode()) +} +``` + +```jsx filename="app/page.js" switcher +import { draftMode } from 'next/headers' + +export default function Page() { + const { isEnabled } = use(draftMode()) +} +``` + ## Version History -| Version | Changes | -| --------- | ----------------------- | -| `v13.4.0` | `draftMode` introduced. | +| Version | Changes | +| ------------ | --------------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `draftMode` is now an async function. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available. | +| `v13.4.0` | `draftMode` introduced. | + +``` + +``` From 8b3f8c366b035f8a02dc52522c3c21d039d75969 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Fri, 13 Sep 2024 14:20:01 +0100 Subject: [PATCH 17/58] Update page.js API reference, add basic examples --- .../02-file-conventions/page.mdx | 158 ++++++++++++++++-- 1 file changed, 143 insertions(+), 15 deletions(-) diff --git a/docs/02-app/02-api-reference/02-file-conventions/page.mdx b/docs/02-app/02-api-reference/02-file-conventions/page.mdx index e1cfa04de45b0..95403a16682ac 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/page.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/page.mdx @@ -3,15 +3,15 @@ title: page.js description: API reference for the page.js file. --- -A **page** is UI that is unique to a route. +The `page` file is used to define a page in your Next.js application. ```tsx filename="app/blog/[slug]/page.tsx" switcher export default function Page({ params, searchParams, }: { - params: { slug: string } - searchParams: { [key: string]: string | string[] | undefined } + params: Promise<{ slug: string }> + searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) { return

My Page

} @@ -23,11 +23,29 @@ export default function Page({ params, searchParams }) { } ``` -## Props +## Reference -### `params` (optional) +### Props -An object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) from the root segment down to that page. For example: +#### `params` (optional) + +A promise that resolves to an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) from the root segment down to that page. For example: + +```tsx filename="app/shop/[slug]/page.tsx" switcher +export default async function Page({ + params, +}: { + params: Promise<{ slug: string }> +}) { + const slug = (await params).slug +} +``` + +```jsx filename="app/shop/[slug]/page.js" switcher +export default async function Page({ params }) { + const slug = (await params).slug +} +``` | Example | URL | `params` | | ------------------------------------ | ----------- | ------------------------------ | @@ -35,9 +53,28 @@ An object containing the [dynamic route parameters](/docs/app/building-your-appl | `app/shop/[category]/[item]/page.js` | `/shop/1/2` | `{ category: '1', item: '2' }` | | `app/shop/[...slug]/page.js` | `/shop/1/2` | `{ slug: ['1', '2'] }` | -### `searchParams` (optional) +- Since the `params` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. + - In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. -An object containing the [search parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters) of the current URL. For example: +#### `searchParams` (optional) + +A promise that resolves to an object containing the [search parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters) of the current URL. For example: + +```tsx filename="app/shop/page.tsx" switcher +export default async function Page({ + searchParams, +}: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }> +}) { + const filters = (await searchParams).filters +} +``` + +```jsx filename="app/shop/page.js" switcher +export default async function Page({ searchParams }) { + const filters = (await searchParams).filters +} +``` | URL | `searchParams` | | --------------- | -------------------- | @@ -45,13 +82,104 @@ An object containing the [search parameters](https://developer.mozilla.org/docs/ | `/shop?a=1&b=2` | `{ a: '1', b: '2' }` | | `/shop?a=1&a=2` | `{ a: ['1', '2'] }` | -> **Good to know**: -> -> - `searchParams` is a **[Dynamic API](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)** at request time. -> - `searchParams` returns a plain JavaScript object and not a `URLSearchParams` instance. +- Since the `searchParams` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. + - In version 14 and earlier, `searchParams` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. +- `searchParams` is a **[Dynamic API](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)** at request time. +- `searchParams` is a plain JavaScript object, not a `URLSearchParams` instance. + +## Examples + +### Displaying content based on `params` + +Using [dynamic route segments](/docs/app/building-your-application/routing/dynamic-routes), you can display or fetch specific content for the page based on the `params` prop. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export default async function Page({ + params, +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = await params + return

Blog Post: {slug}

+} +``` + +```jsx filename="app/blog/[slug]/page.js" switcher +export default async function Page({ params }) { + const { slug } = await params + return

Blog Post: {slug}

+} +``` + +### Handling filtering with `searchParams` + +You can use the `searchParams` prop to handle filtering, pagination, or sorting based on the query string of the URL. + +```tsx filename="app/shop/page.tsx" switcher +export default async function Page({ + searchParams, +}: { + searchParams: Promise<{ [key: string]: string | string[] | undefined }> +}) { + const { page = '1', sort = 'asc', query = '' } = await searchParams + + return ( +
+

Product Listing

+

Search query: {query}

+

Current page: {page}

+

Sort order: {sort}

+
+ ) +} +``` + +```jsx filename="app/shop/page.js" switcher +export default async function Page({ searchParams }) { + const { page = '1', sort = 'asc', query = '' } = await searchParams + + return ( +
+

Product Listing

+

Search query: {query}

+

Current page: {page}

+

Sort order: {sort}

+
+ ) +} +``` + +## Reading `searchParams` and `params` in synchronous components + +To use `searchParams` and `params` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: + +```tsx filename="app/page.tsx" switcher +import { use } from 'react' + +export function Page({ + params, + searchParams, +}: { + params: Promise<{ slug: string }> + searchParams: Promise<{ [key: string]: string | string[] | undefined }> +}) { + const { slug } = use(params) + const { query } = use(searchParams) +} +``` + +```js filename="app/ui/component.js" switcher +import { use } from 'react' + +export function Page({ params, searchParams }) { + const { slug } = use(params) + const { query } = use(searchParams) +} +``` ## Version History -| Version | Changes | -| --------- | ------------------ | -| `v13.0.0` | `page` introduced. | +| Version | Changes | +| ------------ | ----------------------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `params` and `searchParams` are now promises. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available. | +| `v13.0.0` | `page` introduced. | From 84ea124711f58eaa28b254289b2cf817d38209fc Mon Sep 17 00:00:00 2001 From: Delba de Oliveira Date: Mon, 16 Sep 2024 11:22:02 +0100 Subject: [PATCH 18/58] Update layout.js to follow template, add examples --- .../02-file-conventions/layout.mdx | 154 ++++++++++++++---- 1 file changed, 118 insertions(+), 36 deletions(-) diff --git a/docs/02-app/02-api-reference/02-file-conventions/layout.mdx b/docs/02-app/02-api-reference/02-file-conventions/layout.mdx index df018a00dd338..9fbaec13bed35 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/layout.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/layout.mdx @@ -3,7 +3,7 @@ title: layout.js description: API reference for the layout.js file. --- -A **layout** is UI that is shared between routes. +The `layout` file is used to define a layout in your Next.js application. ```tsx filename="app/dashboard/layout.tsx" switcher export default function DashboardLayout({ @@ -47,59 +47,78 @@ export default function RootLayout({ children }) { } ``` -## Props +## Reference -### `children` (required) +### Props -Layout components should accept and use a `children` prop. During rendering, `children` will be populated with the route segments the layout is wrapping. These will primarily be the component of a child [Layout](/docs/app/building-your-application/routing/pages) (if it exists) or [Page](/docs/app/building-your-application/routing/pages), but could also be other special files like [Loading](/docs/app/building-your-application/routing/loading-ui-and-streaming) or [Error](/docs/app/building-your-application/routing/error-handling) when applicable. - -### `params` (optional) +#### `children` (required) -The [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) object from the root segment down to that layout. +Layout components should accept and use a `children` prop. During rendering, `children` will be populated with the route segments the layout is wrapping. These will primarily be the component of a child [Layout](/docs/app/building-your-application/routing/pages) (if it exists) or [Page](/docs/app/building-your-application/routing/pages), but could also be other special files like [Loading](/docs/app/building-your-application/routing/loading-ui-and-streaming) or [Error](/docs/app/building-your-application/routing/error-handling) when applicable. -| Example | URL | `params` | -| --------------------------------- | -------------- | ------------------------- | -| `app/dashboard/[team]/layout.js` | `/dashboard/1` | `{ team: '1' }` | -| `app/shop/[tag]/[item]/layout.js` | `/shop/1/2` | `{ tag: '1', item: '2' }` | -| `app/blog/[...slug]/layout.js` | `/blog/1/2` | `{ slug: ['1', '2'] }` | +#### `params` (optional) -For example: +A promise that resolves to an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) object from the root segment down to that layout. -```tsx filename="app/shop/[tag]/[item]/layout.tsx" switcher -export default function ShopLayout({ - children, +```tsx filename="app/dashboard/[team]/layout.tsx" switcher +export default async function Layout({ params, }: { - children: React.ReactNode - params: { - tag: string - item: string - } + params: Promise<{ team: string }> }) { - // URL -> /shop/shoes/nike-air-max-97 - // `params` -> { tag: 'shoes', item: 'nike-air-max-97' } - return
{children}
+ const team = (await params).team } ``` -```jsx filename="app/shop/[tag]/[item]/layout.js" switcher -export default function ShopLayout({ children, params }) { - // URL -> /shop/shoes/nike-air-max-97 - // `params` -> { tag: 'shoes', item: 'nike-air-max-97' } - return
{children}
+```jsx filename="app/dashboard/[team]/layout.js" switcher +export default async function Layout({ params }) { + const team = (await params).team } ``` -## Good to know +| Example Route | URL | `params` | +| --------------------------------- | -------------- | ---------------------------------- | +| `app/dashboard/[team]/layout.js` | `/dashboard/1` | `Promise<{ team: '1' }>` | +| `app/shop/[tag]/[item]/layout.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` | +| `app/blog/[...slug]/layout.js` | `/blog/1/2` | `Promise<{ slug: ['1', '2'] }>` | + +- Since the `params` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. + - In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. ### Root Layouts -- The `app` directory **must** include a root `app/layout.js`. +The `app` directory **must** include a root `app/layout.js`. + +```tsx filename="app/layout.tsx" switcher +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + - The root layout **must** define `` and `` tags. - - You should **not** manually add `` tags such as `` and `<meta>` to root layouts. Instead, you should use the [Metadata API](/docs/app/api-reference/functions/generate-metadata) which automatically handles advanced requirements such as streaming and de-duplicating `<head>` elements. + - You should **not** manually add `<head>` tags such as `<title>` and `<meta>` to root layouts. Instead, you should use the [Metadata API](/docs/app/building-your-application/optimizing/metadata) which automatically handles advanced requirements such as streaming and de-duplicating `<head>` elements. - You can use [route groups](/docs/app/building-your-application/routing/route-groups) to create multiple root layouts. - Navigating **across multiple root layouts** will cause a **full page load** (as opposed to a client-side navigation). For example, navigating from `/cart` that uses `app/(shop)/layout.js` to `/blog` that uses `app/(marketing)/layout.js` will cause a full page load. This **only** applies to multiple root layouts. +## Caveats + ### Layouts do not receive `searchParams` Unlike [Pages](/docs/app/api-reference/file-conventions/page), Layout components **do not** receive the `searchParams` prop. This is because a shared layout is [not re-rendered during navigation](/docs/app/building-your-application/routing/linking-and-navigating#4-partial-rendering) which could lead to stale `searchParams` between navigations. @@ -122,7 +141,7 @@ This performance optimization allows navigation between pages that share a layou Because `dashboard/layout.tsx` doesn't re-render, the `searchParams` prop in the layout Server Component might become **stale** after navigation. -Instead, use the Page [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop or the [`useSearchParams`](/docs/app/api-reference/functions/use-search-params) hook in a Client Component, which is re-rendered on the client with the latest `searchParams`. +Instead, use the Page [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop or the [`useSearchParams`](/docs/app/api-reference/functions/use-search-params) hook in a Client Component within the layout, which is rerendered on the client with the latest `searchParams`. ### Layouts cannot access `pathname` @@ -162,8 +181,71 @@ Common `pathname` patterns can also be implemented with [`params`](#params-optio See the [examples](/docs/app/building-your-application/routing/layouts-and-templates#examples) section for more information. +## Examples + +### Displaying content based on `params` + +Using [dynamic route segments](/docs/app/building-your-application/routing/dynamic-routes), you can display or fetch specific content based on the `params` prop. + +```tsx filename="app/dashboard/layout.tsx" switcher +export default async function DashboardLayout({ + children, + params, +}: { + children: React.ReactNode + params: Promise<{ team: string }> +}) { + const { team } = await params + + return ( + <section> + <header> + <h1>Welcome to {team}'s Dashboard</h1> + </header> + <main>{children}</main> + </section> + ) +} +``` + +```jsx filename="app/dashboard/layout.js" switcher +export default async function DashboardLayout({ children, params }) { + const { team } = await params + + return ( + <section> + <header> + <h1>Welcome to {team}'s Dashboard</h1> + </header> + <main>{children}</main> + </section> + ) +} +``` + +### Reading `params` in synchronous components + +To use `params` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: + +```tsx filename="app/page.tsx" switcher +import { use } from 'react' + +export function Page({ params }: { params: Promise<{ slug: string }> }) { + const { slug } = use(params) +} +``` + +```js filename="app/page.js" switcher +import { use } from 'react' + +export function Page({ params }) { + const { slug } = use(params) +} +``` + ## Version History -| Version | Changes | -| --------- | -------------------- | -| `v13.0.0` | `layout` introduced. | +| Version | Changes | +| ------------ | ---------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `params` is now a promise. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available. | +| `v13.0.0` | `layout` introduced. | From b85275e0667f95a352f1d08f1a20156939afd245 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 16 Sep 2024 11:22:26 +0100 Subject: [PATCH 19/58] Polish --- .../02-file-conventions/page.mdx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/02-app/02-api-reference/02-file-conventions/page.mdx b/docs/02-app/02-api-reference/02-file-conventions/page.mdx index 95403a16682ac..d492a801ea82b 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/page.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/page.mdx @@ -29,7 +29,7 @@ export default function Page({ params, searchParams }) { #### `params` (optional) -A promise that resolves to an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) from the root segment down to that page. For example: +A promise that resolves to an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) from the root segment down to that page. ```tsx filename="app/shop/[slug]/page.tsx" switcher export default async function Page({ @@ -47,11 +47,11 @@ export default async function Page({ params }) { } ``` -| Example | URL | `params` | -| ------------------------------------ | ----------- | ------------------------------ | -| `app/shop/[slug]/page.js` | `/shop/1` | `{ slug: '1' }` | -| `app/shop/[category]/[item]/page.js` | `/shop/1/2` | `{ category: '1', item: '2' }` | -| `app/shop/[...slug]/page.js` | `/shop/1/2` | `{ slug: ['1', '2'] }` | +| Example Route | URL | `params` | +| ------------------------------------ | ----------- | --------------------------------------- | +| `app/shop/[slug]/page.js` | `/shop/1` | `Promise<{ slug: '1' }>` | +| `app/shop/[category]/[item]/page.js` | `/shop/1/2` | `Promise<{ category: '1', item: '2' }>` | +| `app/shop/[...slug]/page.js` | `/shop/1/2` | `Promise<{ slug: ['1', '2'] }>` | - Since the `params` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. - In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. @@ -76,11 +76,11 @@ export default async function Page({ searchParams }) { } ``` -| URL | `searchParams` | -| --------------- | -------------------- | -| `/shop?a=1` | `{ a: '1' }` | -| `/shop?a=1&b=2` | `{ a: '1', b: '2' }` | -| `/shop?a=1&a=2` | `{ a: ['1', '2'] }` | +| Example URL | `searchParams` | +| --------------- | ----------------------------- | +| `/shop?a=1` | `Promise<{ a: '1' }>` | +| `/shop?a=1&b=2` | `Promise<{ a: '1', b: '2' }>` | +| `/shop?a=1&a=2` | `Promise<{ a: ['1', '2'] }>` | - Since the `searchParams` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. - In version 14 and earlier, `searchParams` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. @@ -149,7 +149,7 @@ export default async function Page({ searchParams }) { } ``` -## Reading `searchParams` and `params` in synchronous components +### Reading `searchParams` and `params` in synchronous components To use `searchParams` and `params` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: @@ -168,7 +168,7 @@ export function Page({ } ``` -```js filename="app/ui/component.js" switcher +```js filename="app/page.js" switcher import { use } from 'react' export function Page({ params, searchParams }) { From 9f2ad438b19a1b6920197f31c67b211525c421dc Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 16 Sep 2024 13:05:37 +0100 Subject: [PATCH 20/58] Update async API examples --- .../01-routing/13-route-handlers.mdx | 12 +++---- .../02-data-fetching/01-fetching.mdx | 2 +- .../03-server-actions-and-mutations.mdx | 12 +++---- .../03-rendering/01-server-components.mdx | 8 ++--- .../03-rendering/04-partial-prerendering.mdx | 22 +++++++----- .../06-optimizing/04-metadata.mdx | 4 +-- .../03-environment-variables.mdx | 2 +- .../07-configuring/11-draft-mode.mdx | 12 +++---- .../15-content-security-policy.mdx | 8 ++--- .../09-authentication/index.mdx | 36 +++++++++---------- .../10-deploying/01-production-checklist.mdx | 2 +- .../10-deploying/index.mdx | 2 +- .../11-upgrading/04-app-router-migration.mdx | 16 ++++----- .../02-file-conventions/not-found.mdx | 4 +-- .../route-segment-config.mdx | 2 +- .../02-api-reference/04-functions/after.mdx | 10 +++--- .../02-api-reference/04-functions/cookies.mdx | 12 +++---- .../04-functions/draft-mode.mdx | 4 +-- .../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 | 8 ++--- .../04-functions/unstable_rethrow.mdx | 4 +-- 23 files changed, 111 insertions(+), 107 deletions(-) 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 e4c18763b484a..0150fc9a82ca9 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 @@ -151,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!', { @@ -165,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!', { @@ -201,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!', { @@ -215,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!', { @@ -270,13 +270,13 @@ export async function GET( request: Request, { params }: { params: { 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' } ``` 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 99a1dfca41614..4e8e013fea935 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 @@ -137,7 +137,7 @@ If you do _not_ want to cache the response from the database, you can add the fo 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 419c0adcf0c26..a252ce528d565 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 @@ -839,13 +839,13 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - const value = cookies().get('name')?.value + await cookies().get('name')?.value // Set cookie - cookies().set('name', 'Delba') + await cookies().set('name', 'Delba') // Delete cookie - cookies().delete('name') + await cookies().delete('name') } ``` @@ -856,13 +856,13 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - const value = cookies().get('name')?.value + await cookies().get('name')?.value // Set cookie - cookies().set('name', 'Delba') + await cookies().set('name', 'Delba') // Delete cookie - cookies().delete('name') + await cookies().delete('name') } ``` 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 6af7be687dab8..8ddf8f29d8861 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 @@ -103,10 +103,10 @@ As a developer, you do not need to choose between static and dynamic rendering a 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: -- [`cookies()`](/docs/app/api-reference/functions/cookies) -- [`headers()`](/docs/app/api-reference/functions/headers) -- [`unstable_noStore()`](/docs/app/api-reference/functions/unstable_noStore) -- [`after()`](/docs/app/api-reference/functions/after): +- [`cookies`](/docs/app/api-reference/functions/cookies) +- [`headers`](/docs/app/api-reference/functions/headers) +- [`unstable_noStore`](/docs/app/api-reference/functions/unstable_noStore) +- [`after`](/docs/app/api-reference/functions/after): - [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional) Using any of these functions will opt the whole route into dynamic rendering at request time. 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..7ec79cea7f9b6 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 @@ -129,13 +129,13 @@ module.exports = nextConfig 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. -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: { sort: string } +}) { + const sort = (await searchParams).sort === 'true' return '...' } ``` ```jsx filename="app/table.js" switcher -export function Table({ searchParams }: { +export async function Table({ searchParams }: { searchParams: { sort: string } }) { - const sort = searchParams.sort === 'true'; + const sort = (await searchParams).sort === 'true'; return '...' } ``` 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..48d510f9ffdb7 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 @@ -61,7 +61,7 @@ export async function generateMetadata( parent: ResolvingMetadata ): Promise<Metadata> { // 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/07-configuring/03-environment-variables.mdx b/docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx index b4c468f7c70af..20dc199b3bd6e 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 @@ -196,7 +196,7 @@ import { unstable_noStore as noStore } from 'next/cache' export default function Component() { noStore() - // cookies(), headers(), and other dynamic functions + // cookies, headers, and other dynamic functions // will also opt into dynamic rendering, meaning // this env variable is evaluated at runtime const value = process.env.MY_VALUE 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 44daa91281700..fc3caaa73bd67 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,7 @@ 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() + await draftMode().enable() return new Response('Draft mode is enabled') } ``` @@ -43,7 +43,7 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - draftMode().enable() + await draftMode().enable() return new Response('Draft mode is enabled') } ``` @@ -99,7 +99,7 @@ export async function GET(request: Request) { } // Enable Draft Mode by setting the cookie - draftMode().enable() + await draftMode().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 +133,7 @@ export async function GET(request) { } // Enable Draft Mode by setting the cookie - draftMode().enable() + await draftMode().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 +156,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 +184,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..6c7dabd8adfae 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 ( <Script @@ -194,8 +194,8 @@ export default function Page() { 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 ( <Script diff --git a/docs/02-app/01-building-your-application/09-authentication/index.mdx b/docs/02-app/01-building-your-application/09-authentication/index.mdx index a36d09595d174..d57a1abcbb7e3 100644 --- a/docs/02-app/01-building-your-application/09-authentication/index.mdx +++ b/docs/02-app/01-building-your-application/09-authentication/index.mdx @@ -550,7 +550,7 @@ To create and manage stateless sessions, there are a few steps you need to follo 1. Generate a secret key, which will be used to sign your session, and store it as an [environment variable](/docs/app/building-your-application/configuring/environment-variables). 2. Write logic to encrypt/decrypt session data using a session management library. -3. Manage cookies using the Next.js [`cookies()`](/docs/app/api-reference/functions/cookies) API. +3. Manage cookies using the Next.js [`cookies`](/docs/app/api-reference/functions/cookies) API. In addition to the above, consider adding functionality to [update (or refresh)](#updating-or-refreshing-sessions) the session when the user returns to the application, and [delete](#deleting-the-session) the session when the user logs out. @@ -641,7 +641,7 @@ export async function decrypt(session) { #### 3. Setting cookies (recommended options) -To store the session in a cookie, use the Next.js [`cookies()`](/docs/app/api-reference/functions/cookies) API. The cookie should be set on the server, and include the recommended options: +To store the session in a cookie, use the Next.js [`cookies`](/docs/app/api-reference/functions/cookies) API. The cookie should be set on the server, and include the recommended options: - **HttpOnly**: Prevents client-side JavaScript from accessing the cookie. - **Secure**: Use https to send the cookie. @@ -659,7 +659,7 @@ export async function createSession(userId: string) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) const session = await encrypt({ userId, expiresAt }) - cookies().set('session', session, { + await cookies().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -677,7 +677,7 @@ export async function createSession(userId) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) const session = await encrypt({ userId, expiresAt }) - cookies().set('session', session, { + await cookies().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -738,7 +738,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export async function updateSession() { - const session = cookies().get('session')?.value + const session = await cookies().get('session')?.value const payload = await decrypt(session) if (!session || !payload) { @@ -746,7 +746,7 @@ export async function updateSession() { } const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - cookies().set('session', session, { + await cookies().set('session', session, { httpOnly: true, secure: true, expires: expires, @@ -762,7 +762,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export async function updateSession() { - const session = cookies().get('session').value + const session = await cookies().get('session').value const payload = await decrypt(session) if (!session || !payload) { @@ -770,7 +770,7 @@ export async function updateSession() { } const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - cookies().set('session', session, { + await cookies().set('session', session, { httpOnly: true, secure: true, expires: expires, @@ -790,8 +790,8 @@ To delete the session, you can delete the cookie: import 'server-only' import { cookies } from 'next/headers' -export function deleteSession() { - cookies().delete('session') +export async function deleteSession() { + await cookies().delete('session') } ``` @@ -799,8 +799,8 @@ export function deleteSession() { import 'server-only' import { cookies } from 'next/headers' -export function deleteSession() { - cookies().delete('session') +export async function deleteSession() { + await cookies().delete('session') } ``` @@ -911,7 +911,7 @@ export async function createSession(id: number) { const session = await encrypt({ sessionId, expiresAt }) // 3. Store the session in cookies for optimistic auth checks - cookies().set('session', session, { + await cookies().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -945,7 +945,7 @@ export async function createSession(id) { const session = await encrypt({ sessionId, expiresAt }) // 3. Store the session in cookies for optimistic auth checks - cookies().set('session', session, { + await cookies().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -1056,7 +1056,7 @@ export default async function middleware(req: NextRequest) { const isPublicRoute = publicRoutes.includes(path) // 3. Decrypt the session from the cookie - const cookie = cookies().get('session')?.value + const cookie = await cookies().get('session')?.value const session = await decrypt(cookie) // 5. Redirect to /login if the user is not authenticated @@ -1098,7 +1098,7 @@ export default async function middleware(req) { const isPublicRoute = publicRoutes.includes(path) // 3. Decrypt the session from the cookie - const cookie = cookies().get('session')?.value + const cookie = await cookies().get('session')?.value const session = await decrypt(cookie) // 5. Redirect to /login if the user is not authenticated @@ -1149,7 +1149,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export const verifySession = cache(async () => { - const cookie = cookies().get('session')?.value + const cookie = await cookies().get('session')?.value const session = await decrypt(cookie) if (!session?.userId) { @@ -1167,7 +1167,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export const verifySession = cache(async () => { - const cookie = cookies().get('session').value + const cookie = await cookies().get('session').value const session = await decrypt(cookie) if (!session.userId) { diff --git a/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx b/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx index efc0a236093f4..e5ca60fd7dfa1 100644 --- a/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx @@ -43,7 +43,7 @@ While building your application, we recommend using the following features to en - **[`<Link>` component](/docs/app/building-your-application/routing/linking-and-navigating#link-component):** Use the `<Link>` component for [client-side navigation and prefetching](/docs/app/building-your-application/routing/linking-and-navigating#how-routing-and-navigation-works). - **[Error Handling](/docs/app/building-your-application/routing/error-handling):** Gracefully handle [catch-all errors](/docs/app/building-your-application/routing/error-handling) and [404 errors](/docs/app/api-reference/file-conventions/not-found) in production by creating custom error pages. - **[Composition Patterns](/docs/app/building-your-application/rendering/composition-patterns):** Follow the recommended composition patterns for Server and Client Components, and check the placement of your [`"use client"` boundaries](/docs/app/building-your-application/rendering/composition-patterns#moving-client-components-down-the-tree) to avoid unnecessarily increasing your client-side JavaScript bundle. -- **[Dynamic Functions](/docs/app/building-your-application/rendering/server-components#dynamic-functions):** Be aware that dynamic functions like [`cookies()`](/docs/app/api-reference/functions/cookies) and the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop will opt the entire route into [Dynamic Rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) (or your whole application if used in the [Root Layout](/docs/app/building-your-application/routing/layouts-and-templates#root-layout-required)). Ensure dynamic function usage is intentional and wrap them in `<Suspense>` boundaries where appropriate. +- **[Dynamic Functions](/docs/app/building-your-application/rendering/server-components#dynamic-functions):** Be aware that dynamic functions like [`cookies`](/docs/app/api-reference/functions/cookies) and the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop will opt the entire route into [Dynamic Rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) (or your whole application if used in the [Root Layout](/docs/app/building-your-application/routing/layouts-and-templates#root-layout-required)). Ensure dynamic function usage is intentional and wrap them in `<Suspense>` boundaries where appropriate. > **Good to know**: [Partial Prerendering (experimental)](/blog/next-14#partial-prerendering-preview) will allow parts of a route to be dynamic without opting the whole route into dynamic rendering. diff --git a/docs/02-app/01-building-your-application/10-deploying/index.mdx b/docs/02-app/01-building-your-application/10-deploying/index.mdx index 25a54dc53f270..f3a44e40bd5f0 100644 --- a/docs/02-app/01-building-your-application/10-deploying/index.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/index.mdx @@ -105,7 +105,7 @@ import { unstable_noStore as noStore } from 'next/cache'; export default function Component() { noStore(); - // cookies(), headers(), and other dynamic functions + // cookies, headers, and other dynamic functions // will also opt into dynamic rendering, making // this env variable is evaluated at runtime const value = process.env.MY_VALUE diff --git a/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx b/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx index 7d26326b0f25e..e0218c45bceba 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx @@ -623,23 +623,23 @@ export default function Page(props) { The `app` directory exposes new read-only functions to retrieve request data: -- [`headers()`](/docs/app/api-reference/functions/headers): Based on the Web Headers API, and can be used inside [Server Components](/docs/app/building-your-application/rendering/server-components) to retrieve request headers. -- [`cookies()`](/docs/app/api-reference/functions/cookies): Based on the Web Cookies API, and can be used inside [Server Components](/docs/app/building-your-application/rendering/server-components) to retrieve cookies. +- [`headers`](/docs/app/api-reference/functions/headers): Based on the Web Headers API, and can be used inside [Server Components](/docs/app/building-your-application/rendering/server-components) to retrieve request headers. +- [`cookies`](/docs/app/api-reference/functions/cookies): Based on the Web Cookies API, and can be used inside [Server Components](/docs/app/building-your-application/rendering/server-components) to retrieve cookies. ```tsx filename="app/page.tsx" switcher // `app` directory import { cookies, headers } from 'next/headers' async function getData() { - const authHeader = headers().get('authorization') + const authHeader = await headers().get('authorization') return '...' } export default async function Page() { - // You can use `cookies()` or `headers()` inside Server Components + // You can use `cookies` or `headers` inside Server Components // directly or in your data fetching function - const theme = cookies().get('theme') + const theme = await cookies().get('theme') const data = await getData() return '...' } @@ -650,15 +650,15 @@ export default async function Page() { import { cookies, headers } from 'next/headers' async function getData() { - const authHeader = headers().get('authorization') + const authHeader = await headers().get('authorization') return '...' } export default async function Page() { - // You can use `cookies()` or `headers()` inside Server Components + // You can use `cookies` or `headers` inside Server Components // directly or in your data fetching function - const theme = cookies().get('theme') + const theme = await cookies().get('theme') const data = await getData() return '...' } diff --git a/docs/02-app/02-api-reference/02-file-conventions/not-found.mdx b/docs/02-app/02-api-reference/02-file-conventions/not-found.mdx index 6ecce2046d225..dd23e63c4f06d 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/not-found.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/not-found.mdx @@ -48,7 +48,7 @@ import Link from 'next/link' import { headers } from 'next/headers' export default async function NotFound() { - const headersList = headers() + const headersList = await headers() const domain = headersList.get('host') const data = await getSiteData(domain) return ( @@ -68,7 +68,7 @@ import Link from 'next/link' import { headers } from 'next/headers' export default async function NotFound() { - const headersList = headers() + const headersList = await headers() const domain = headersList.get('host') const data = await getSiteData(domain) return ( diff --git a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx index 59af5855dd34e..1dbfe0e424ac9 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx @@ -60,7 +60,7 @@ export const dynamic = 'auto' - Setting the option of every `fetch()` request in a layout or page to `{ cache: 'force-cache' }`. - Setting the segment config to `fetchCache = 'only-cache', dynamicParams = false`. - `dynamic = 'error'` changes the default of `dynamicParams` from `true` to `false`. You can opt back into dynamically rendering pages for dynamic params not generated by `generateStaticParams` by manually setting `dynamicParams = true`. -- **`'force-static'`**: Force static rendering and cache the data of a layout or page by forcing [`cookies()`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers) and [`useSearchParams()`](/docs/app/api-reference/functions/use-search-params) to return empty values. +- **`'force-static'`**: Force static rendering and cache the data of a layout or page by forcing [`cookies`](/docs/app/api-reference/functions/cookies), [`headers()`](/docs/app/api-reference/functions/headers) and [`useSearchParams()`](/docs/app/api-reference/functions/use-search-params) to return empty values. > **Good to know**: > diff --git a/docs/02-app/02-api-reference/04-functions/after.mdx b/docs/02-app/02-api-reference/04-functions/after.mdx index 8de314133fb4d..d0600fe228c72 100644 --- a/docs/02-app/02-api-reference/04-functions/after.mdx +++ b/docs/02-app/02-api-reference/04-functions/after.mdx @@ -38,11 +38,11 @@ export default function Layout({ children }) { > **Good to know**: > -> - `after()` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound()` or `redirect()` is called. -> - `after()` is a [dynamic function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. -> - You can use React `cache` to deduplicate functions called inside `after()`. -> - [`cookies()`](/docs/app/api-reference/functions/cookies) cannot be set inside `after()` since the response has already been sent. -> - `after()` can be nested inside other `after()` calls. +> - `after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called. +> - `after` is a [dynamic function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. +> - You can use React `cache` to deduplicate functions called inside `after`. +> - [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `after` since the response has already been sent. +> - `after` can be nested inside other `after` calls. ## Parameters diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index c035496f48c7a..f6416fe1c54c2 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -64,13 +64,13 @@ To learn more about these options, see the [MDN docs](https://developer.mozilla. ## Good to know -- `cookies()` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access cookies. - - In version 14 and earlier, `cookies()` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. -- `cookies()` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). -- The `.delete()` method can only be called: +- `cookies` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access cookies. + - In version 14 and earlier, `cookies` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. +- `cookies` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). +- The `.delete` method can only be called: - In a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). - - If it belongs to the same domain from which `.set()` is called. Additionally, the code must be executed on the same protocol (HTTP or HTTPS) as the cookie you want to delete. -- HTTP does not allow setting cookies after streaming starts, so you must use `.set()` in a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). + - If it belongs to the same domain from which `.set` is called. Additionally, the code must be executed on the same protocol (HTTP or HTTPS) as the cookie you want to delete. +- HTTP does not allow setting cookies after streaming starts, so you must use `.set` in a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). ## Examples diff --git a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx index ce0d5e672e210..b5812e9aabdc7 100644 --- a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx +++ b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx @@ -38,8 +38,8 @@ The following methods and properties are available: ## Good to know -- `draftMode()` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. - - In version 14 and earlier, `draftMode()` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. +- `draftMode` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. + - In version 14 and earlier, `draftMode` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. - A new bypass cookie value will be generated each time you run `next build`. This ensures that the bypass cookie can’t be guessed. - To test Draft Mode locally over HTTP, your browser will need to allow third-party cookies and local storage access. diff --git a/docs/02-app/02-api-reference/04-functions/generate-image-metadata.mdx b/docs/02-app/02-api-reference/04-functions/generate-image-metadata.mdx index f9b43525f9c8e..c9b27dece993e 100644 --- a/docs/02-app/02-api-reference/04-functions/generate-image-metadata.mdx +++ b/docs/02-app/02-api-reference/04-functions/generate-image-metadata.mdx @@ -164,7 +164,7 @@ export default async function Image({ params: { id: string } id: number }) { - const productId = params.id + const productId = (await params).id const imageId = id const text = await getCaptionForImage(productId, imageId) @@ -200,7 +200,7 @@ export async function generateImageMetadata({ params }) { } export default async function Image({ params, id }) { - const productId = params.id + const productId = (await params).id const imageId = id const text = await getCaptionForImage(productId, imageId) diff --git a/docs/02-app/02-api-reference/04-functions/generate-metadata.mdx b/docs/02-app/02-api-reference/04-functions/generate-metadata.mdx index 08078d5ea77bf..918a0bdfc42c5 100644 --- a/docs/02-app/02-api-reference/04-functions/generate-metadata.mdx +++ b/docs/02-app/02-api-reference/04-functions/generate-metadata.mdx @@ -90,7 +90,7 @@ export async function generateMetadata( parent: ResolvingMetadata ): Promise<Metadata> { // read route params - const id = params.id + const id = (await params).id // fetch data const product = await fetch(`https://.../${id}`).then((res) => res.json()) @@ -112,7 +112,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/02-api-reference/04-functions/generate-static-params.mdx b/docs/02-app/02-api-reference/04-functions/generate-static-params.mdx index 60c4e3bcd2419..217d40e0aa992 100644 --- a/docs/02-app/02-api-reference/04-functions/generate-static-params.mdx +++ b/docs/02-app/02-api-reference/04-functions/generate-static-params.mdx @@ -17,8 +17,8 @@ export async function generateStaticParams() { // Multiple versions of this page will be statically generated // using the `params` returned by `generateStaticParams` -export default function Page({ params }) { - const { slug } = params +export default async function Page({ params }) { + const { slug } = await params // ... } ``` @@ -65,8 +65,8 @@ export function generateStaticParams() { // - /product/1 // - /product/2 // - /product/3 -export default function Page({ params }: { params: { id: string } }) { - const { id } = params +export default async function Page({ params }: { params: { id: string } }) { + const { id } = await params // ... } ``` @@ -81,8 +81,8 @@ export function generateStaticParams() { // - /product/1 // - /product/2 // - /product/3 -export default function Page({ params }) { - const { id } = params +export default async function Page({ params }) { + const { id } = await params // ... } ``` @@ -103,12 +103,12 @@ export function generateStaticParams() { // - /products/a/1 // - /products/b/2 // - /products/c/3 -export default function Page({ +export default async function Page({ params, }: { params: { category: string; product: string } }) { - const { category, product } = params + const { category, product } = await params // ... } ``` @@ -127,8 +127,8 @@ export function generateStaticParams() { // - /products/a/1 // - /products/b/2 // - /products/c/3 -export default function Page({ params }) { - const { category, product } = params +export default async function Page({ params }) { + const { category, product } = await params // ... } ``` @@ -145,8 +145,8 @@ export function generateStaticParams() { // - /product/a/1 // - /product/b/2 // - /product/c/3 -export default function Page({ params }: { params: { slug: string[] } }) { - const { slug } = params +export default async function Page({ params }: { params: { slug: string[] } }) { + const { slug } = await params // ... } ``` @@ -161,8 +161,8 @@ export function generateStaticParams() { // - /product/a/1 // - /product/b/2 // - /product/c/3 -export default function Page({ params }) { - const { slug } = params +export default async function Page({ params }) { + const { slug } = await params // ... } ``` diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index b8285531525d8..90959cdcd238d 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -42,10 +42,10 @@ export default async function Page() { ## Good to know -- `headers()` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. - - In version 14 and earlier, `headers()` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. -- Since `headers()` is read-only, you cannot `set` or `delete` the outgoing request headers. -- `headers()` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)**. +- `headers` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. + - In version 14 and earlier, `headers` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. +- Since `headers` is read-only, you cannot `set` or `delete` the outgoing request headers. +- `headers` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)**. ## Examples diff --git a/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx b/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx index 7a35302c22358..dad4a2c3e65cf 100644 --- a/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx +++ b/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx @@ -51,8 +51,8 @@ The following Next.js APIs rely on throwing an error which should be rethrown an If a route segment is marked to throw an error unless it's static, a dynamic function call will also throw an error that should similarly not be caught by the developer. Note that Partial Prerendering (PPR) affects this behavior as well. These APIs are: -- [`cookies()`](/docs/app/api-reference/functions/cookies) -- [`headers()`](/docs/app/api-reference/functions/headers) +- [`cookies`](/docs/app/api-reference/functions/cookies) +- [`headers`](/docs/app/api-reference/functions/headers) - [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) - `fetch(..., { cache: 'no-store' })` - `fetch(..., { next: { revalidate: 0 } })` From 8cf00d77285aaba389f192128a24819dc2e54de7 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 16 Sep 2024 13:25:47 +0100 Subject: [PATCH 21/58] Update noStore example to use connection --- .../03-environment-variables.mdx | 33 +++++++++++++++-- .../10-deploying/index.mdx | 37 ++++++++++++++++--- 2 files changed, 60 insertions(+), 10 deletions(-) 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 20dc199b3bd6e..cce3e57c8aecb 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,13 +189,34 @@ 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. +<PagesOnly> + +To read runtime environment variables, we recommend using `getServerSideProps` or [incrementally adopting the App Router](/docs/app/building-your-application/upgrading/app-router-migration). + +</PagesOnly> + +<AppOnly> + +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 function Component() { + await connection() + // cookies, headers, and other dynamic functions + // 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() + await connection() // cookies, headers, and other dynamic functions // will also opt into dynamic rendering, meaning // this env variable is evaluated at runtime @@ -204,6 +225,10 @@ export default function Component() { } ``` +</AppOnly> + +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/10-deploying/index.mdx b/docs/02-app/01-building-your-application/10-deploying/index.mdx index e67810a79a85b..5e3131b63de12 100644 --- a/docs/02-app/01-building-your-application/10-deploying/index.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/index.mdx @@ -98,21 +98,46 @@ 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. +<PagesOnly> + +To read runtime environment variables, we recommend using `getServerSideProps` or [incrementally adopting the App Router](/docs/app/building-your-application/upgrading/app-router-migration). + +</PagesOnly> + +<AppOnly> + +You safely read environment variables on the server during dynamic rendering. + +```tsx filename="app/page.ts" switcher +import { connection } from 'next/server' + +export default function Component() { + await connection() + // cookies, headers, and other dynamic functions + // 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(); + await connection() // cookies, headers, and other dynamic functions - // will also opt into dynamic rendering, making + // will also opt into dynamic rendering, meaning // this env variable is evaluated at runtime const value = process.env.MY_VALUE - ... + // ... } ``` +</AppOnly> + +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). From 9f62cf5575ac218bc418fc836f6f90c4a55ff476 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Tue, 17 Sep 2024 10:12:34 +0100 Subject: [PATCH 22/58] Update router handler params to async --- .../01-routing/13-route-handlers.mdx | 12 +-- .../02-file-conventions/route.mdx | 102 +++++++++++++----- 2 files changed, 80 insertions(+), 34 deletions(-) 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 62c806df8a008..243632ae5813b 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 @@ -270,7 +270,7 @@ 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 = (await params).slug // 'a', 'b', or 'c' } @@ -282,11 +282,11 @@ export async function GET(request, { params }) { } ``` -| 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/02-api-reference/02-file-conventions/route.mdx b/docs/02-app/02-api-reference/02-file-conventions/route.mdx index 9ef5f7702286e..f9eaabdfafb48 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/route.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/route.mdx @@ -5,7 +5,21 @@ description: API reference for the route.js special file. Route Handlers allow you to create custom request handlers for a given route using the Web [Request](https://developer.mozilla.org/docs/Web/API/Request) and [Response](https://developer.mozilla.org/docs/Web/API/Response) APIs. -## HTTP Methods +```ts filename="route.ts" switcher +export async function GET() { + return new Response.json({ message: 'Hello World' }) +} +``` + +```js filename="route.js" switcher +export async function GET() { + return new Response.json({ message: 'Hello World' }) +} +``` + +## Reference + +### HTTP Methods A **route** file allows you to create custom request handlers for a given route. The following [HTTP methods](https://developer.mozilla.org/docs/Web/HTTP/Methods) are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS`. @@ -43,51 +57,83 @@ export async function PATCH(request) {} export async function OPTIONS(request) {} ``` -> **Good to know**: Route Handlers are only available inside the App Router. You **do not** need to use API Routes (`pages`) and Route Handlers (`app`) together, as Route Handlers should be able to handle all use cases. - -## Parameters +### Parameters -### `request` (optional) +#### `request` (optional) The `request` object is a [NextRequest](/docs/app/api-reference/functions/next-request) object, which is an extension of the Web [Request](https://developer.mozilla.org/docs/Web/API/Request) API. `NextRequest` gives you further control over the incoming request, including easily accessing `cookies` and an extended, parsed, URL object `nextUrl`. -### `context` (optional) +```ts filename="route.ts" switcher +import type { NextRequest } from 'next/server' -```ts filename="app/dashboard/[team]/route.ts" switcher -type Params = { - team: string +export async function GET(request: NextRequest) { + const url = request.nextUrl } +``` -export async function GET(request: Request, context: { params: Params }) { - const team = context.params.team // '1' +```js filename="route.js" switcher +export async function GET(request) { + const url = request.nextUrl } - -// Define params type according to your route parameters (see table below) ``` -```js filename="app/dashboard/[team]/route.js" switcher +#### `context` (optional) -export async function GET(request, context: { params }) { - const team = context.params.team // '1' +- **`params`**: a promise that resolves to an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) for the current route. + +```ts filename="app/dashboard/[team]/route.ts" switcher +export async function GET( + request: Request, + { params }: { params: Promise<{ team: string }> } +) { + const team = (await params).team } +``` +```js filename="app/dashboard/[team]/route.js" switcher +export async function GET(request, { params }) { + const team = (await params).team +} ``` -Currently, the only value of `context` is `params`, which is an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) for the current route. +| Example | URL | `params` | +| -------------------------------- | -------------- | ---------------------------------- | +| `app/dashboard/[team]/route.js` | `/dashboard/1` | `Promise<{ team: '1' }>` | +| `app/shop/[tag]/[item]/route.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` | +| `app/blog/[...slug]/route.js` | `/blog/1/2` | `Promise<{ slug: ['1', '2'] }>` | + +## Examples + +### Handling cookies + +```ts filename="route.ts" switcher +import { cookies } from 'next/headers' + +export async function GET(request: NextRequest) { + const cookieStore = await cookies() + + const a = cookieStore.get('a') + const b = cookieStore.set('b', '1') + const c = cookieStore.delete('c') +} +``` -| Example | URL | `params` | -| -------------------------------- | -------------- | ------------------------- | -| `app/dashboard/[team]/route.js` | `/dashboard/1` | `{ team: '1' }` | -| `app/shop/[tag]/[item]/route.js` | `/shop/1/2` | `{ tag: '1', item: '2' }` | -| `app/blog/[...slug]/route.js` | `/blog/1/2` | `{ slug: ['1', '2'] }` | +```js filename="route.js" switcher +import { cookies } from 'next/headers' -## NextResponse +export async function GET(request) { + const cookieStore = await cookies() -Route Handlers can extend the Web Response API by returning a `NextResponse` object. This allows you to easily set cookies, headers, redirect, and rewrite. [View the API reference](/docs/app/api-reference/functions/next-response). + const a = cookieStore.get('a') + const b = cookieStore.set('b', '1') + const c = cookieStore.delete('c') +} +``` ## Version History -| Version | Changes | -| --------- | ------------------------------------------------------------------------- | -| `v15.0.0` | The default caching for `GET` handlers was changed from static to dynamic | -| `v13.2.0` | Route Handlers are introduced. | +| Version | Changes | +| ------------ | ----------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `context.params` is now a promise. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available | +| `v15.0.0-RC` | The default caching for `GET` handlers was changed from static to dynamic | +| `v13.2.0` | Route Handlers are introduced. | From 0bafb707a2cb71847ab0c4aec3fb7afe744b698d Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Tue, 17 Sep 2024 14:46:58 +0100 Subject: [PATCH 23/58] Promote `turbo` option to stable --- .../05-next-config-js/turbo.mdx | 64 ++++++++----------- 1 file changed, 25 insertions(+), 39 deletions(-) diff --git a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx index 701e20f02bf40..3d98ddc9e94e7 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx @@ -1,7 +1,6 @@ --- title: turbo description: Configure Next.js with Turbopack-specific options -version: experimental --- {/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} @@ -12,10 +11,8 @@ The `turbo` option allows you customize [Turbopack](/docs/architecture/turbopack import type { NextConfig } from 'next' const nextConfig: NextConfig = { - experimental: { - turbo: { - // ... - }, + turbo: { + // ... }, } @@ -25,10 +22,8 @@ export default nextConfig ```js filename="next.config.js" switcher /** @type {import('next').NextConfig} */ const nextConfig = { - experimental: { - turbo: { - // ... - }, + turbo: { + // ... }, } @@ -81,20 +76,18 @@ To configure loaders, add the names of the loaders you've installed and any opti ```js filename="next.config.js" module.exports = { - experimental: { - turbo: { - rules: { - '*.svg': { - loaders: ['@svgr/webpack'], - as: '*.js', - }, + turbo: { + rules: { + '*.svg': { + loaders: ['@svgr/webpack'], + as: '*.js', }, }, }, } ``` -> **Good to know**: Prior to Next.js version 13.4.4, `experimental.turbo.rules` was named `experimental.turbo.loaders` and only accepted file extensions like `.mdx` instead of `*.mdx`. +> **Good to know**: Prior to Next.js version 13.4.4, `turbo.rules` was named `turbo.loaders` and only accepted file extensions like `.mdx` instead of `*.mdx`. ### Resolving aliases @@ -104,12 +97,10 @@ To configure resolve aliases, map imported patterns to their new destination in ```js filename="next.config.js" module.exports = { - experimental: { - turbo: { - resolveAlias: { - underscore: 'lodash', - mocha: { browser: 'mocha/browser-entry.js' }, - }, + turbo: { + resolveAlias: { + underscore: 'lodash', + mocha: { browser: 'mocha/browser-entry.js' }, }, }, } @@ -127,18 +118,8 @@ To configure resolve extensions, use the `resolveExtensions` field in `next.conf ```js filename="next.config.js" module.exports = { - experimental: { - turbo: { - resolveExtensions: [ - '.mdx', - '.tsx', - '.ts', - '.jsx', - '.js', - '.mjs', - '.json', - ], - }, + turbo: { + resolveExtensions: ['.mdx', '.tsx', '.ts', '.jsx', '.js', '.mjs', '.json'], }, } ``` @@ -160,10 +141,15 @@ To configure the module IDs strategy, use the `moduleIdStrategy` field in `next. ```js filename="next.config.js" module.exports = { - experimental: { - turbo: { - moduleIdStrategy: 'deterministic', - }, + turbo: { + moduleIdStrategy: 'deterministic', }, } ``` + +## Version History + +| Version | Changes | +| ----------- | ------------------------------------------------- | +| `15.0.0-RC` | `turbo` is now stable and no longer experimental. | +| `13.0.0` | `experimental.turbo` introduced. | From 371f3df6e356d022a8396d85be2b58de2afa1941 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Tue, 17 Sep 2024 14:52:04 +0100 Subject: [PATCH 24/58] Clarify `use` usage --- .../02-file-conventions/layout.mdx | 8 +++++-- .../02-file-conventions/page.mdx | 8 +++++-- .../02-api-reference/04-functions/cookies.mdx | 22 ------------------- .../04-functions/draft-mode.mdx | 18 --------------- .../02-api-reference/04-functions/headers.mdx | 22 ------------------- 5 files changed, 12 insertions(+), 66 deletions(-) diff --git a/docs/02-app/02-api-reference/02-file-conventions/layout.mdx b/docs/02-app/02-api-reference/02-file-conventions/layout.mdx index 9fbaec13bed35..dc0933a108b8d 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/layout.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/layout.mdx @@ -223,11 +223,13 @@ export default async function DashboardLayout({ children, params }) { } ``` -### Reading `params` in synchronous components +### Reading `params` in Client Components -To use `params` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: +To use `params` in a Client Component (which cannot be `async`), you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: ```tsx filename="app/page.tsx" switcher +'use client' + import { use } from 'react' export function Page({ params }: { params: Promise<{ slug: string }> }) { @@ -236,6 +238,8 @@ export function Page({ params }: { params: Promise<{ slug: string }> }) { ``` ```js filename="app/page.js" switcher +'use client' + import { use } from 'react' export function Page({ params }) { diff --git a/docs/02-app/02-api-reference/02-file-conventions/page.mdx b/docs/02-app/02-api-reference/02-file-conventions/page.mdx index d492a801ea82b..f4c5a4ef93790 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/page.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/page.mdx @@ -149,11 +149,13 @@ export default async function Page({ searchParams }) { } ``` -### Reading `searchParams` and `params` in synchronous components +### Reading `searchParams` and `params` in Client Components -To use `searchParams` and `params` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: +To use `searchParams` and `params` in a Client Component (which cannot be `async`), you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: ```tsx filename="app/page.tsx" switcher +'use client' + import { use } from 'react' export function Page({ @@ -169,6 +171,8 @@ export function Page({ ``` ```js filename="app/page.js" switcher +'use client' + import { use } from 'react' export function Page({ params, searchParams }) { diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index f6416fe1c54c2..7678c8468485f 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -266,28 +266,6 @@ export async function delete(data) { } ``` -### Cookies in synchronous components - -To use `cookies` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: - -```tsx filename="app/ui/component.tsx" switcher -import { cookies } from 'next/headers' -import { use } from 'react' - -export default function Component() { - const theme = use(cookies()).get('theme') -} -``` - -```js filename="app/ui/component.js" switcher -import { cookies } from 'next/headers' -import { use } from 'react' - -export default function Component() { - const theme = use(cookies()).get('theme') -} -``` - ## Version History | Version | Changes | diff --git a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx index b5812e9aabdc7..fb11ca0060c46 100644 --- a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx +++ b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx @@ -125,24 +125,6 @@ export default async function Page() { } ``` -For synchronous components, you can use the React [`use`](https://react.dev/reference/react/use) function: - -```tsx filename="app/page.tsx" switcher -import { draftMode } from 'next/headers' - -export default function Page() { - const { isEnabled } = use(draftMode()) -} -``` - -```jsx filename="app/page.js" switcher -import { draftMode } from 'next/headers' - -export default function Page() { - const { isEnabled } = use(draftMode()) -} -``` - ## Version History | Version | Changes | diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index 90959cdcd238d..c11396c522b49 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -65,28 +65,6 @@ export default async function Page() { } ``` -### Reading headers in synchronous components - -To use `cookies` in a synchronous Server Component, you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise: - -```tsx filename="app/ui/component.tsx" switcher -import { headers } from 'next/headers' -import { use } from 'react' - -export function Component() { - const userAgent = use(headers().get('user-agent')) -} -``` - -```js filename="app/ui/component.js" switcher -import { cookies } from 'next/headers' -import { use } from 'react' - -export function Component() { - const userAgent = use(headers().get('user-agent')) -} -``` - ## Version History | Version | Changes | From 6eb58f90032761e097c0d2da1e805c856e0db0c5 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Tue, 17 Sep 2024 17:21:24 +0100 Subject: [PATCH 25/58] Update upgrade guide (wip) --- .../11-upgrading/02-version-15.mdx | 245 +++++++++++++++++- 1 file changed, 241 insertions(+), 4 deletions(-) diff --git a/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx b/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx index 337eff87f04b4..58678e98db226 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx @@ -30,16 +30,253 @@ bun add next@rc react@rc react-dom@rc eslint-config-next@rc > - If you see a peer dependencies warning, you may need to update `react` and `react-dom` to the suggested versions, or you use the `--force` or `--legacy-peer-deps` flag to ignore the warning. This won't be necessary once both Next.js 15 and React 19 are stable. > - If you are using TypeScript, you'll need to temporarily override the React types. See the [React 19 RC upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide#installing) for more information. -## Minimum React version - -- The minimum `react` and `react-dom` is now 19. - ## React 19 +- The minimum `react` and `react-dom` is now 19. - `useFormState` has been replaced by `useActionState`. The `useFormState` hook is still available in React 19, but it is deprecated and will be removed in a future release. `useActionState` is recommended and includes additional properties like reading the `pending` state directly. [Learn more](https://react.dev/reference/react/useActionState). - `useFormStatus` now includes additional keys like `data`, `method`, and `action`. If you are not using React 19, only the `pending` key is available. [Learn more](https://react.dev/reference/react-dom/hooks/useFormStatus). - Read more in the [React 19 upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide). +## Async Request APIs + +Dynamic APIs that rely on runtime information, are now **asynchronous**. These APIs can temporarily be accessed both synchronously and asynchronously, and a codemod is available to automate migration. + +The following APIs are now asynchronous: + +- [`cookies`](/docs/app/api-reference/functions/cookies) +- [`headers`](/docs/app/api-reference/functions/headers) +- [`draftMode`](/docs/app/api-reference/functions/draft-mode) +- `params` in [`layout.js`](/docs/app/api-reference/file-conventions/layout), [`page.js`](/docs/app/api-reference/file-conventions/page), [`route.js`](/docs/app/api-reference/file-conventions/route), [`default.js`](/docs/app/api-reference/file-conventions/default), [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons), and [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons). +- `searchParams` in [`page.js`](/docs/app/api-reference/file-conventions/page) + +### Codemod + +... + +### `cookies` + +```tsx +import { cookies } from 'next/headers' + +export default async function Page() { + // Before + const cookieStore = cookies() + const token = cookieStore.get('token') + + // After + const cookieStore = await cookies() + const token = cookieStore.get('token') +} +``` + +### `headers` + +```tsx +import { headers } from 'next/headers' + +export default async function Page() { + // Before + const headersList = headers() + const type = headersList.get('user-agent') + + // After + const headersList = await headers() + const type = headersList.get('user-agent') +} +``` + +### `draftMode` + +```tsx +import { draftMode } from 'next/headers' + +export default async function Page() { + // Before + const { isEnabled } = draftMode() + + // After + const { isEnabled } = await draftMode() +} +``` + +### `params` + +`layout` + +```tsx +// Before +type Params = { slug: string } +export function generateMetadata({ params }: { params: Params }) { + const slug = params.slug +} +export default function Layout({ + children, + params, +}: { + children: React.ReactNode + params: Params +}) { + const slug = params.slug +} + +// After +type Params = Promise<{ slug: string }> +export async function generateMetadata({ params }: { params: Params }) { + const slug = (await params).slug +} +export default async function Layout({ + children, + params, +}: { + children: React.ReactNode + params: Params +}) { + const slug = (await params).slug +} +``` + +`page` + +```tsx +// Before +type Params = { slug: string } +export function generateMetadata({ params }: { params: Params }) { + const slug = params.slug +} +export default function Page({ params }: { params: Params }) { + const slug = params.slug +} + +// After +type Params = Promise<{ slug: string }> +export async function generateMetadata({ params }: { params: Params }) { + const slug = (await params).slug +} +export default async function Page({ params }: { params: Params }) { + const slug = (await params).slug +} +``` + +`layout` and `page` (Client Component) + +```tsx +'use client' + +// Before +type Params = { slug: string } +export default function Page({ params }: { params: Params }) { + const slug = params.slug +} + +// After +import { use } from 'react' +type Params = Promise<{ slug: string }> +export default function Page({ params }: { params: Params }) { + const slug = use(params).slug +} +``` + +`default` + +```tsx +// Before +type Params = { slug: string } +export default function Default({ params }: { params: Params }) { + const slug = params.slug +} + +// After +type Params = Promise<{ slug: string }> +export default async function Default({ params }: { params: Params }) { + const slug = (await params).slug +} +``` + +`route` + +```tsx +// Before +type Params = { slug: string } +export async function GET(request: Request, { params }: { params: Params }) { + const slug = params.slug +} + +// After +type Params = Promise<{ slug: string }> +export async function GET(request: Request, { params }: { params: Params }) { + const slug = (await params).slug +} +``` + +`opengraph-image`, `twitter-image`, `icon` and `apple-icon` + +```tsx +// Before +type Params = { slug: string } +export function generateImageMetadata({ params }: { params: Params }) {} +export function Image({ params }: { params: Params }) { + const slug = params.slug +} + +// After +type Params = Promise<{ slug: string }> +export async function generateImageMetadata({ params }: { params: Params }) {} +export async function Image({ params }: { params: Params }) { + const slug = (await params).slug +} +``` + +### `searchParams` + +`page` + +```tsx +// Before +type SearchParams = { [key: string]: string | string[] | undefined } +export function generateMetadata({ + searchParams, +}: { + searchParams: SearchParams +}) { + const query = searchParams.query +} +export default function Page({ searchParams }: { searchParams: SearchParams }) { + const query = searchParams.query +} + +// After +type SearchParams = Promise<{ [key: string]: string | string[] | undefined }> +export async function generateMetadata({ + searchParams, +}: { + searchParams: SearchParams +}) { + const query = (await searchParams).query +} +export async function Page({ searchParams }: { searchParams: SearchParams }) { + const query = (await searchParams).query +} +``` + +`page` (Client Component) + +```tsx +'use client' + +// Before +type SearchParams = { [key: string]: string | string[] | undefined } +export default function Page({ searchParams }: { searchParams: SearchParams }) { + const query = searchParams.query +} + +// After +import { use } from 'react' +type SearchParams = Promise<{ [key: string]: string | string[] | undefined }> +export default function Page({ searchParams }: { searchParams: SearchParams }) { + const query = use(searchParams).query +} +``` + ## `fetch` requests [`fetch` requests](/docs/app/api-reference/functions/fetch) are no longer cached by default. From aebbf70fde7eddc6db5c3854aa93f201ab57e7f8 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 11:44:06 +0100 Subject: [PATCH 26/58] Add note about secure server actions --- .../02-data-fetching/03-server-actions-and-mutations.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 614cce15d336b..e69af30696346 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 @@ -870,9 +870,13 @@ See [additional examples](/docs/app/api-reference/functions/cookies#deleting-coo ## Security +When you add the `"use server"` directive to the top of a file, it marks all the exports in that file as API endpoints. By default, exported but unused functions are tree-shaken. That is, Next.js evaluates your codebase and removes references to unused Server Actions from the client-side JavaScript bundle. + +In addition, the IDs that link the client-side reference to the server-side code is re-encrypted on each new deployment. This means Server Actions can only be invoked internally by the application. + ### 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 treat Server Actions as public-facing API endpoints, and ensure that the user is authorized to perform the action. For example: ```tsx filename="app/actions.ts" 'use server' From 9b86e0e46ad8619c8d554ccd25fc1193e10ff2e0 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 11:50:42 +0100 Subject: [PATCH 27/58] Update upgrade guide - Add JS blocks - Add TS temporary usage --- .../11-upgrading/02-version-15.mdx | 327 +++++++++++++++--- 1 file changed, 281 insertions(+), 46 deletions(-) diff --git a/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx b/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx index 58678e98db226..502830ac7c1e2 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx @@ -37,36 +37,45 @@ bun add next@rc react@rc react-dom@rc eslint-config-next@rc - `useFormStatus` now includes additional keys like `data`, `method`, and `action`. If you are not using React 19, only the `pending` key is available. [Learn more](https://react.dev/reference/react-dom/hooks/useFormStatus). - Read more in the [React 19 upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide). -## Async Request APIs +## Async Request APIs (Breaking change) -Dynamic APIs that rely on runtime information, are now **asynchronous**. These APIs can temporarily be accessed both synchronously and asynchronously, and a codemod is available to automate migration. +Previously synchronous Dynamic APIs that rely on runtime information are now **asynchronous**: -The following APIs are now asynchronous: +- [`cookies`](https://www.notion.so/docs/app/api-reference/functions/cookies) +- [`headers`](https://www.notion.so/docs/app/api-reference/functions/headers) +- [`draftMode`](https://www.notion.so/docs/app/api-reference/functions/draft-mode) +- `params` in [`layout.js`](https://www.notion.so/docs/app/api-reference/file-conventions/layout), [`page.js`](https://www.notion.so/docs/app/api-reference/file-conventions/page), [`route.js`](https://www.notion.so/docs/app/api-reference/file-conventions/route), [`default.js`](https://www.notion.so/docs/app/api-reference/file-conventions/default), [`opengraph-image`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`twitter-image`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`icon`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/app-icons), and [`apple-icon`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/app-icons). +- `searchParams` in [`page.js`](https://www.notion.so/docs/app/api-reference/file-conventions/page) -- [`cookies`](/docs/app/api-reference/functions/cookies) -- [`headers`](/docs/app/api-reference/functions/headers) -- [`draftMode`](/docs/app/api-reference/functions/draft-mode) -- `params` in [`layout.js`](/docs/app/api-reference/file-conventions/layout), [`page.js`](/docs/app/api-reference/file-conventions/page), [`route.js`](/docs/app/api-reference/file-conventions/route), [`default.js`](/docs/app/api-reference/file-conventions/default), [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons), and [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons). -- `searchParams` in [`page.js`](/docs/app/api-reference/file-conventions/page) - -### Codemod - -... +To ease the burden of migration, a [codemod is available](https://www.notion.so/docs/app/building-your-application/upgrading/codemods#150) to automate the process and the APIs can temporarily be accessed synchronously. ### `cookies` ```tsx import { cookies } from 'next/headers' -export default async function Page() { - // Before - const cookieStore = cookies() - const token = cookieStore.get('token') +// Before +const cookieStore = cookies() +const token = cookieStore.get('token') - // After - const cookieStore = await cookies() - const token = cookieStore.get('token') -} +// After (Preferred usage) +const cookieStore = await cookies() +const token = cookieStore.get('token') + +// After (Temporary supported API) +import { type DangerouslyUnwrapCookies } from 'next/headers' +const cookieStore = cookies() as unknown as DangerouslyUnwrapCookies +const token = cookieStore.get('token') +``` + +```jsx +// Before +const cookieStore = cookies() +const token = cookieStore.get('token') + +// After +const cookieStore = await cookies() +const token = cookieStore.get('token') ``` ### `headers` @@ -74,15 +83,28 @@ export default async function Page() { ```tsx import { headers } from 'next/headers' -export default async function Page() { - // Before - const headersList = headers() - const type = headersList.get('user-agent') +// Before +const headersList = headers() +const userAgent = headersList.get('user-agent') - // After - const headersList = await headers() - const type = headersList.get('user-agent') -} +// After (Preferred usage) +const headersList = await headers() +const userAgent = headersList.get('user-agent') + +// After (Temporary supported API) +import { type DangerouslyUnwrapHeaders } from 'next/headers' +const headersList = headers() as unknown as DangerouslyUnwrapHeaders +const userAgent = headersList.get('user-agent') +``` + +```jsx +// Before +const headersList = headers() +const userAgent = headersList.get('user-agent') + +// After +const headersList = await headers() +const userAgent = headersList.get('user-agent') ``` ### `draftMode` @@ -90,13 +112,23 @@ export default async function Page() { ```tsx import { draftMode } from 'next/headers' -export default async function Page() { - // Before - const { isEnabled } = draftMode() +// Before +const { isEnabled } = draftMode() - // After - const { isEnabled } = await draftMode() -} +// After (Preferred usage) +const { isEnabled } = await draftMode() + +// After (Temporary supported API) +import { type DangerouslyUnwrapDraftMode } from 'next/headers' +const { isEnabled } = draftMode() as unknown as DangerouslyUnwrapDraftMode +``` + +```jsx +// Before +const { isEnabled } = draftMode() + +// After +const { isEnabled } = await draftMode() ``` ### `params` @@ -119,7 +151,7 @@ export default function Layout({ const slug = params.slug } -// After +// After (Preferred usage) type Params = Promise<{ slug: string }> export async function generateMetadata({ params }: { params: Params }) { const slug = (await params).slug @@ -133,6 +165,43 @@ export default async function Layout({ }) { const slug = (await params).slug } + +// After (Temporary supported API) +import { type DangerouslyUnwrapParams } from 'next/headers' +type Params = Promise<{ slug: string }> +export async function generateMetadata({ params }: { params: Params }) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} +export default async function Layout({ + children, + params, +}: { + children: React.ReactNode + params: Params +}) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} +``` + +```jsx +// Before +export function generateMetadata({ params }) { + const slug = params.slug +} +export default function Layout({ params }) { + const slug = params.slug +} + +// After +export async function generateMetadata({ params }) { + const slug = (await params).slug +} +export default async function Layout() { + const slug = (await params).slug +} + ``` `page` @@ -147,7 +216,7 @@ export default function Page({ params }: { params: Params }) { const slug = params.slug } -// After +// After (Preferred usage) type Params = Promise<{ slug: string }> export async function generateMetadata({ params }: { params: Params }) { const slug = (await params).slug @@ -155,6 +224,36 @@ export async function generateMetadata({ params }: { params: Params }) { export default async function Page({ params }: { params: Params }) { const slug = (await params).slug } + +// After (Temporary supported API) +import { type DangerouslyUnwrapParams } from 'next/headers' +type Params = Promise<{ slug: string }> +export async function generateMetadata({ params }: { params: Params }) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} +export default async function Page({ params }: { params: Params }) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} +``` + +```jsx +// Before +export function generateMetadata({ params }) { + const slug = params.slug +} +export default function Page({ params }) { + const slug = params.slug +} + +// After +export async function generateMetadata({ params }) { + const slug = (await params).slug +} +export async function Page({ params }) { + const slug = (await params).slug +} ``` `layout` and `page` (Client Component) @@ -176,36 +275,85 @@ export default function Page({ params }: { params: Params }) { } ``` +```jsx +// Before +export default function Page({ params }) { +const slug = params.slug +} + +// After +import { use } from "react" +export default function Page({ params }) { +const slug = use(params).slug +} + +``` + `default` ```tsx // Before -type Params = { slug: string } +type Params = { artist: string } export default function Default({ params }: { params: Params }) { - const slug = params.slug + const artist = params.artist } // After -type Params = Promise<{ slug: string }> +type Params = Promise<{ artist: string }> export default async function Default({ params }: { params: Params }) { - const slug = (await params).slug + const artist = (await params).artist } ``` +```jsx +// Before +export default function Default({ params }) { + const artist = params.artist +} + +// After +export default async function Default({ params }) { + const artist = (await params).artist +} + +``` + `route` -```tsx +``` // Before type Params = { slug: string } export async function GET(request: Request, { params }: { params: Params }) { const slug = params.slug } -// After +// After (Preferred usage) type Params = Promise<{ slug: string }> export async function GET(request: Request, { params }: { params: Params }) { const slug = (await params).slug } + +// After (Temporary supported API) +import { type DangerouslyUnwrapParams } from "next/headers" +type Params = Promise<{ slug: string }> +export async function GET(request: Request, { params }: { params: Params }) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} + +``` + +``` +// Before +export async function GET(request, { params }) { + const slug = params.slug +} + +// After +export async function GET(request, { params }) { + const slug = (await params).slug +} + ``` `opengraph-image`, `twitter-image`, `icon` and `apple-icon` @@ -213,17 +361,51 @@ export async function GET(request: Request, { params }: { params: Params }) { ```tsx // Before type Params = { slug: string } -export function generateImageMetadata({ params }: { params: Params }) {} +export function generateImageMetadata({ params }: { params: Params }) { + const slug = params.slug +} export function Image({ params }: { params: Params }) { const slug = params.slug } -// After +// After (Preferred usage) type Params = Promise<{ slug: string }> -export async function generateImageMetadata({ params }: { params: Params }) {} +export async function generateImageMetadata({ params }: { params: Params }) { + const slug = (await params).slug +} export async function Image({ params }: { params: Params }) { const slug = (await params).slug } + +// After (Temporary supported API) +import { type DangerouslyUnwrapParams } from 'next/headers' +type Params = Promise<{ slug: string }> +export async function generateImageMetadata({ params }: { params: Params }) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} +export async function Image({ params }: { params: Params }) { + const syncParams = params as unknown as DangerouslyUnwrapParams<typeof params> + const slug = syncParams.slug +} +``` + +```jsx +// Before +export function generateImageMetadata({ params }) { + const slug = params.slug +} +export function Image({ params }) { + const slug = params.slug +} + +// After +export async function generateImageMetadata({ params }) { + const slug = (await params).slug +} +export async function Image({ params }) { + const slug = (await params).slug +} ``` ### `searchParams` @@ -244,7 +426,7 @@ export default function Page({ searchParams }: { searchParams: SearchParams }) { const query = searchParams.query } -// After +// After (Preferred usage) type SearchParams = Promise<{ [key: string]: string | string[] | undefined }> export async function generateMetadata({ searchParams, @@ -256,6 +438,46 @@ export async function generateMetadata({ export async function Page({ searchParams }: { searchParams: SearchParams }) { const query = (await searchParams).query } + +// After (Temporary supported API) +import { type DangerouslyUnwrapSearchParams } from 'next/headers' +type SearchParams = Promise<{ [key: string]: string | string[] | undefined }> +export async function generateMetadata({ + searchParams, +}: { + searchParams: SearchParams +}) { + const syncSearchParams = + searchParams as unknown as DangerouslyUnwrapSearchParams< + typeof searchParams + > + const query = syncSearchParams.query +} +export async function Page({ searchParams }: { searchParams: SearchParams }) { + const syncSearchParams = + searchParams as unknown as DangerouslyUnwrapSearchParams< + typeof searchParams + > + const query = syncSearchParams.query +} +``` + +```jsx +// Before +export function generateMetadata({ searchParams }) { + const query = searchParams.query +} +export default function Page({ searchParams }) { + const query = searchParams.query +} + +// After +export async function generateMetadata({ searchParams }) { + const query = (await searchParams).query +} +export async function Page({ searchParams }) { + const query = (await searchParams).query +} ``` `page` (Client Component) @@ -277,6 +499,19 @@ export default function Page({ searchParams }: { searchParams: SearchParams }) { } ``` +```jsx +// Before +export default function Page({ searchParams }) { + const query = searchParams.query +} + +// After +export default function Page({ searchParams }) { + const query = use(searchParams).query +} + +``` + ## `fetch` requests [`fetch` requests](/docs/app/api-reference/functions/fetch) are no longer cached by default. From 1a13e50f6c2ef4a09e4bc93e6b8c7cf3906b9ac3 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 11:57:56 +0100 Subject: [PATCH 28/58] Update default.js reference --- .../02-file-conventions/default.mdx | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/docs/02-app/02-api-reference/02-file-conventions/default.mdx b/docs/02-app/02-api-reference/02-file-conventions/default.mdx index b1275af15d817..eca38db9ef485 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/default.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/default.mdx @@ -27,13 +27,32 @@ On refresh, Next.js will render a `default.js` for `@analytics`. If `default.js` Additionally, since `children` is an implicit slot, you also need to create a `default.js` file to render a fallback for `children` when Next.js cannot recover the active state of the parent page. -## Props +## Reference ### `params` (optional) -An object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) from the root segment down to the slot's subpages. For example: - -| Example | URL | `params` | -| ------------------------------------------ | ------------ | ----------------------------------- | -| `app/[artist]/@sidebar/default.js` | `/zack` | `{ artist: 'zack' }` | -| `app/[artist]/[album]/@sidebar/default.js` | `/zack/next` | `{ artist: 'zack', album: 'next' }` | +A promise that resolves to an object containing the [dynamic route parameters](/docs/app/building-your-application/routing/dynamic-routes) from the root segment down to the slot's subpages. For example: + +```tsx filename="app/[artist]/@sidebar/default.js" switcher +export default async function Default({ + params, +}: { + params: Promise<{ artist: string }> +}) { + const artist = (await params).artist +} +``` + +```jsx filename="app/[artist]/@sidebar/default.js" switcher +export default async function Default({ params }) { + const artist = (await params).artist +} +``` + +| Example | URL | `params` | +| ------------------------------------------ | ------------ | -------------------------------------------- | +| `app/[artist]/@sidebar/default.js` | `/zack` | `Promise<{ artist: 'zack' }>` | +| `app/[artist]/[album]/@sidebar/default.js` | `/zack/next` | `Promise<{ artist: 'zack', album: 'next' }>` | + +- Since the `params` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. + - In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. From 5de2e75e7c2c26911e7aafd8ec612bf876573bd6 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 12:02:31 +0100 Subject: [PATCH 29/58] Remove unecessary Dynamic Functions heading - This was coming up as duplicate in search. When people search dynamic functions, we want to direct them to a specific page --- .../01-routing/13-route-handlers.mdx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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 243632ae5813b..2a8510dbf15c7 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 @@ -139,11 +139,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. @@ -193,7 +189,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. From 29e5b244a514c4115c8cba4a1043d163dfd7afa5 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 13:50:01 +0100 Subject: [PATCH 30/58] Update dynamic APIs section --- .../03-rendering/01-server-components.mdx | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) 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 8ddf8f29d8861..cf2a80114a5cb 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,7 +82,7 @@ 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 functions and data caching affect whether a route is statically or dynamically rendered: | Dynamic Functions | Data | Route | | ----------------- | ---------- | -------------------- | @@ -99,17 +95,17 @@ In the table above, for a route to be fully static, all data must be cached. How 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) -- [`after`](/docs/app/api-reference/functions/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 From e2d78f464da0d9d4655238939d5f5caaa9c2de6d Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 14:00:20 +0100 Subject: [PATCH 31/58] Fix broken links --- .../11-upgrading/02-version-15.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx b/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx index 502830ac7c1e2..f50b0fae4d4f8 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/02-version-15.mdx @@ -41,13 +41,13 @@ bun add next@rc react@rc react-dom@rc eslint-config-next@rc Previously synchronous Dynamic APIs that rely on runtime information are now **asynchronous**: -- [`cookies`](https://www.notion.so/docs/app/api-reference/functions/cookies) -- [`headers`](https://www.notion.so/docs/app/api-reference/functions/headers) -- [`draftMode`](https://www.notion.so/docs/app/api-reference/functions/draft-mode) -- `params` in [`layout.js`](https://www.notion.so/docs/app/api-reference/file-conventions/layout), [`page.js`](https://www.notion.so/docs/app/api-reference/file-conventions/page), [`route.js`](https://www.notion.so/docs/app/api-reference/file-conventions/route), [`default.js`](https://www.notion.so/docs/app/api-reference/file-conventions/default), [`opengraph-image`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`twitter-image`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`icon`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/app-icons), and [`apple-icon`](https://www.notion.so/docs/app/api-reference/file-conventions/metadata/app-icons). -- `searchParams` in [`page.js`](https://www.notion.so/docs/app/api-reference/file-conventions/page) +- [`cookies`](/docs/app/api-reference/functions/cookies) +- [`headers`](/docs/app/api-reference/functions/headers) +- [`draftMode`](/docs/app/api-reference/functions/draft-mode) +- `params` in [`layout.js`](/docs/app/api-reference/file-conventions/layout), [`page.js`](/docs/app/api-reference/file-conventions/page), [`route.js`](/docs/app/api-reference/file-conventions/route), [`default.js`](/docs/app/api-reference/file-conventions/default), [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image), [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons), and [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons). +- `searchParams` in [`page.js`](/docs/app/api-reference/file-conventions/page) -To ease the burden of migration, a [codemod is available](https://www.notion.so/docs/app/building-your-application/upgrading/codemods#150) to automate the process and the APIs can temporarily be accessed synchronously. +To ease the burden of migration, a [codemod is available](/docs/app/building-your-application/upgrading/codemods#150) to automate the process and the APIs can temporarily be accessed synchronously. ### `cookies` @@ -632,7 +632,7 @@ To continue using Speed Insights, follow the [Vercel Speed Insights Quickstart]( ## `NextRequest` Geolocation -The `geo` and `ip` properties on `NextRequest` have been removed. These values had to be provided by your hosting provider. +The `geo` and `ip` properties on `NextRequest` have been removed as these values are provided by your hosting provider. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available to automate this migration. If you are using Vercel, you can alternatively use the `geolocation` and `ipAddress` functions from [`@vercel/functions](https://vercel.com/docs/functions/vercel-functions-package) instead: From 66398dcf2d3909b5c8350ece1ccfa0990a96a85a Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 14:04:39 +0100 Subject: [PATCH 32/58] Replace dynamic functions w/ dynamic APIs to better match the types that can be dynamic. e.g. searchParams is a prop. --- .../01-routing/13-route-handlers.mdx | 2 +- .../02-data-fetching/01-fetching.mdx | 4 ++-- .../03-rendering/01-server-components.mdx | 16 ++++++++-------- .../03-rendering/04-partial-prerendering.mdx | 2 +- .../04-caching/index.mdx | 8 ++++---- .../07-configuring/03-environment-variables.mdx | 4 ++-- .../10-deploying/01-production-checklist.mdx | 2 +- .../10-deploying/index.mdx | 4 ++-- .../01-metadata/app-icons.mdx | 4 ++-- .../02-file-conventions/01-metadata/manifest.mdx | 2 +- .../01-metadata/opengraph-image.mdx | 4 ++-- .../02-file-conventions/01-metadata/robots.mdx | 2 +- .../02-file-conventions/01-metadata/sitemap.mdx | 2 +- .../02-file-conventions/page.mdx | 2 +- .../02-file-conventions/route-segment-config.mdx | 14 +++++++------- .../02-api-reference/04-functions/after.mdx | 2 +- .../02-api-reference/04-functions/connection.mdx | 2 +- .../02-api-reference/04-functions/cookies.mdx | 2 +- .../02-api-reference/04-functions/headers.mdx | 2 +- .../04-functions/unstable_rethrow.mdx | 2 +- .../02-api-reference/04-functions/use-router.mdx | 2 +- .../05-next-config-js/devIndicators.mdx | 2 +- 22 files changed, 43 insertions(+), 43 deletions(-) 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 2a8510dbf15c7..1b4a5afd2e1cc 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 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 dff900825dc98..adcfc601393dd 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). If you do _not_ want to cache the response from the database, you can add the following to your file: 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 cf2a80114a5cb..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 @@ -82,14 +82,14 @@ Dynamic rendering is useful when a route has data that is personalized to the us #### Switching to Dynamic Rendering -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 functions 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 | +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 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. 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 7ec79cea7f9b6..aaa6014865c29 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,7 +127,7 @@ 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`: 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 3caeb8965b5f7..2ddc256b8285c 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. @@ -386,7 +386,7 @@ The following table provides an overview of how different Next.js APIs affect ca | [`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 | | | +| [`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/07-configuring/03-environment-variables.mdx b/docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx index cce3e57c8aecb..13885b7046fa7 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 @@ -204,7 +204,7 @@ import { connection } from 'next/server' export default function Component() { await connection() - // cookies, headers, and other dynamic functions + // 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 @@ -217,7 +217,7 @@ import { connection } from 'next/server' export default function Component() { await connection() - // cookies, headers, and other dynamic functions + // 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 diff --git a/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx b/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx index e5ca60fd7dfa1..b20252c787f18 100644 --- a/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/01-production-checklist.mdx @@ -43,7 +43,7 @@ While building your application, we recommend using the following features to en - **[`<Link>` component](/docs/app/building-your-application/routing/linking-and-navigating#link-component):** Use the `<Link>` component for [client-side navigation and prefetching](/docs/app/building-your-application/routing/linking-and-navigating#how-routing-and-navigation-works). - **[Error Handling](/docs/app/building-your-application/routing/error-handling):** Gracefully handle [catch-all errors](/docs/app/building-your-application/routing/error-handling) and [404 errors](/docs/app/api-reference/file-conventions/not-found) in production by creating custom error pages. - **[Composition Patterns](/docs/app/building-your-application/rendering/composition-patterns):** Follow the recommended composition patterns for Server and Client Components, and check the placement of your [`"use client"` boundaries](/docs/app/building-your-application/rendering/composition-patterns#moving-client-components-down-the-tree) to avoid unnecessarily increasing your client-side JavaScript bundle. -- **[Dynamic Functions](/docs/app/building-your-application/rendering/server-components#dynamic-functions):** Be aware that dynamic functions like [`cookies`](/docs/app/api-reference/functions/cookies) and the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop will opt the entire route into [Dynamic Rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) (or your whole application if used in the [Root Layout](/docs/app/building-your-application/routing/layouts-and-templates#root-layout-required)). Ensure dynamic function usage is intentional and wrap them in `<Suspense>` boundaries where appropriate. +- **[Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-apis):** Be aware that Dynamic APIs like [`cookies`](/docs/app/api-reference/functions/cookies) and the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) prop will opt the entire route into [Dynamic Rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) (or your whole application if used in the [Root Layout](/docs/app/building-your-application/routing/layouts-and-templates#root-layout-required)). Ensure Dynamic API usage is intentional and wrap them in `<Suspense>` boundaries where appropriate. > **Good to know**: [Partial Prerendering (experimental)](/blog/next-14#partial-prerendering-preview) will allow parts of a route to be dynamic without opting the whole route into dynamic rendering. diff --git a/docs/02-app/01-building-your-application/10-deploying/index.mdx b/docs/02-app/01-building-your-application/10-deploying/index.mdx index 5e3131b63de12..e7602d7e2455d 100644 --- a/docs/02-app/01-building-your-application/10-deploying/index.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/index.mdx @@ -113,7 +113,7 @@ import { connection } from 'next/server' export default function Component() { await connection() - // cookies, headers, and other dynamic functions + // 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 @@ -126,7 +126,7 @@ import { connection } from 'next/server' export default function Component() { await connection() - // cookies, headers, and other dynamic functions + // 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 diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx index a6b2d39117cc3..41e4af2890d99 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/app-icons.mdx @@ -166,10 +166,10 @@ export default function Icon() { > **Good to know**: > -> - By default, generated icons are [**statically optimized**](/docs/app/building-your-application/rendering/server-components#static-rendering-default) (generated at build time and cached) unless they use [dynamic functions](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) or uncached data. +> - By default, generated icons are [**statically optimized**](/docs/app/building-your-application/rendering/server-components#static-rendering-default) (generated at build time and cached) unless they use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-apis) or uncached data. > - You can generate multiple icons in the same file using [`generateImageMetadata`](/docs/app/api-reference/functions/generate-image-metadata). > - You cannot generate a `favicon` icon. Use [`icon`](#icon) or a [favicon.ico](#favicon) file instead. -> - App icons are special Route Handlers that is cached by default unless it uses a [dynamic function](/docs/app/building-your-application/caching#dynamic-functions) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. +> - App icons are special Route Handlers that is cached by default unless it uses a [Dynamic API](/docs/app/building-your-application/caching#dynamic-apis) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. ### Props diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/manifest.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/manifest.mdx index 24942e0918f4b..ce0f84af009af 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/manifest.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/manifest.mdx @@ -21,7 +21,7 @@ Add or generate a `manifest.(json|webmanifest)` file that matches the [Web Manif Add a `manifest.js` or `manifest.ts` file that returns a [`Manifest` object](#manifest-object). -> Good to know: `manifest.js` is special Route Handlers that is cached by default unless it uses a [dynamic function](/docs/app/building-your-application/caching#dynamic-functions) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. +> Good to know: `manifest.js` is special Route Handlers that is cached by default unless it uses a [Dynamic API](/docs/app/building-your-application/caching#dynamic-apis) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. ```ts filename="app/manifest.ts" switcher import type { MetadataRoute } from 'next' diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx index f4215a794741c..68d8f71e36aa4 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/opengraph-image.mdx @@ -84,9 +84,9 @@ Generate a route segment's shared image by creating an `opengraph-image` or `twi > **Good to know**: > -> - By default, generated images are [**statically optimized**](/docs/app/building-your-application/rendering/server-components#static-rendering-default) (generated at build time and cached) unless they use [dynamic functions](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) or uncached data. +> - By default, generated images are [**statically optimized**](/docs/app/building-your-application/rendering/server-components#static-rendering-default) (generated at build time and cached) unless they use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-apis) or uncached data. > - You can generate multiple Images in the same file using [`generateImageMetadata`](/docs/app/api-reference/functions/generate-image-metadata). -> - `opengraph-image.js` and `twitter-image.js` are special Route Handlers that is cached by default unless it uses a [dynamic function](/docs/app/building-your-application/caching#dynamic-functions) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. +> - `opengraph-image.js` and `twitter-image.js` are special Route Handlers that is cached by default unless it uses a [Dynamic API](/docs/app/building-your-application/caching#dynamic-apis) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. The easiest way to generate an image is to use the [ImageResponse](/docs/app/api-reference/functions/image-response) API from `next/og`. diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/robots.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/robots.mdx index 3d822bb4499a1..a21f5426b653c 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/robots.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/robots.mdx @@ -19,7 +19,7 @@ Sitemap: https://acme.com/sitemap.xml Add a `robots.js` or `robots.ts` file that returns a [`Robots` object](#robots-object). -> **Good to know**: `robots.js` is a special Route Handlers that is cached by default unless it uses a [dynamic function](/docs/app/building-your-application/caching#dynamic-functions) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. +> **Good to know**: `robots.js` is a special Route Handlers that is cached by default unless it uses a [Dynamic API](/docs/app/building-your-application/caching#dynamic-apis) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. ```ts filename="app/robots.ts" switcher import type { MetadataRoute } from 'next' diff --git a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx index 32342d172f8c2..c56d27f671911 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/01-metadata/sitemap.mdx @@ -41,7 +41,7 @@ For smaller applications, you can create a `sitemap.xml` file and place it in th You can use the `sitemap.(js|ts)` file convention to programmatically **generate** a sitemap by exporting a default function that returns an array of URLs. If using TypeScript, a [`Sitemap`](#returns) type is available. -> **Good to know**: `sitemap.js` is a special Route Handlers that is cached by default unless it uses a [dynamic function](/docs/app/building-your-application/caching#dynamic-functions) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. +> **Good to know**: `sitemap.js` is a special Route Handlers that is cached by default unless it uses a [Dynamic API](/docs/app/building-your-application/caching#dynamic-apis) or [dynamic config](/docs/app/building-your-application/caching#segment-config-options) option. ```ts filename="app/sitemap.ts" switcher import type { MetadataRoute } from 'next' diff --git a/docs/02-app/02-api-reference/02-file-conventions/page.mdx b/docs/02-app/02-api-reference/02-file-conventions/page.mdx index f4c5a4ef93790..65b79ae95c2f6 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/page.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/page.mdx @@ -84,7 +84,7 @@ export default async function Page({ searchParams }) { - Since the `searchParams` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values. - In version 14 and earlier, `searchParams` was a synchronous prop. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. -- `searchParams` is a **[Dynamic API](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)** at request time. +- `searchParams` is a **[Dynamic API](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-apis)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)** at request time. - `searchParams` is a plain JavaScript object, not a `URLSearchParams` instance. ## Examples diff --git a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx index 4cd2491f83f5a..123d302107127 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx @@ -55,7 +55,7 @@ export const dynamic = 'auto' - Setting the option of every `fetch()` request in a layout or page to `{ cache: 'no-store', next: { revalidate: 0 } }`. - Setting the segment config to `export const fetchCache = 'force-no-store'` -- **`'error'`**: Force static rendering and cache the data of a layout or page by causing an error if any components use [dynamic functions](/docs/app/building-your-application/rendering/server-components#dynamic-functions) or uncached data. This option is equivalent to: +- **`'error'`**: Force static rendering and cache the data of a layout or page by causing an error if any components use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-apis) or uncached data. This option is equivalent to: - `getStaticProps()` in the `pages` directory. - Setting the option of every `fetch()` request in a layout or page to `{ cache: 'force-cache' }`. - Setting the segment config to `fetchCache = 'only-cache', dynamicParams = false`. @@ -102,8 +102,8 @@ export const revalidate = false // false | 0 | number ``` -- **`false`** (default): The default heuristic to cache any `fetch` requests that set their `cache` option to `'force-cache'` or are discovered before a [dynamic function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) is used. Semantically equivalent to `revalidate: Infinity` which effectively means the resource should be cached indefinitely. It is still possible for individual `fetch` requests to use `cache: 'no-store'` or `revalidate: 0` to avoid being cached and make the route dynamically rendered. Or set `revalidate` to a positive number lower than the route default to increase the revalidation frequency of a route. -- **`0`**: Ensure a layout or page is always [dynamically rendered](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) even if no dynamic functions or uncached data fetches are discovered. This option changes the default of `fetch` requests that do not set a `cache` option to `'no-store'` but leaves `fetch` requests that opt into `'force-cache'` or use a positive `revalidate` as is. +- **`false`** (default): The default heuristic to cache any `fetch` requests that set their `cache` option to `'force-cache'` or are discovered before a [Dynamic API](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-apis) is used. Semantically equivalent to `revalidate: Infinity` which effectively means the resource should be cached indefinitely. It is still possible for individual `fetch` requests to use `cache: 'no-store'` or `revalidate: 0` to avoid being cached and make the route dynamically rendered. Or set `revalidate` to a positive number lower than the route default to increase the revalidation frequency of a route. +- **`0`**: Ensure a layout or page is always [dynamically rendered](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) even if no Dynamic APIs or uncached data fetches are discovered. This option changes the default of `fetch` requests that do not set a `cache` option to `'no-store'` but leaves `fetch` requests that opt into `'force-cache'` or use a positive `revalidate` as is. - **`number`**: (in seconds) Set the default revalidation frequency of a layout or page to `n` seconds. > **Good to know**: @@ -122,7 +122,7 @@ export const revalidate = false <details> <summary>This is an advanced option that should only be used if you specifically need to override the default behavior.</summary> -By default, Next.js **will cache** any `fetch()` requests that are reachable **before** any [dynamic functions](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) are used and **will not cache** `fetch` requests that are discovered **after** dynamic functions are used. +By default, Next.js **will cache** any `fetch()` requests that are reachable **before** any [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-apis) are used and **will not cache** `fetch` requests that are discovered **after** Dynamic APIs are used. `fetchCache` allows you to override the default `cache` option of all `fetch` requests in a layout or page. @@ -138,11 +138,11 @@ export const fetchCache = 'auto' // 'force-cache' | 'force-no-store' | 'default-no-store' | 'only-no-store' ``` -- **`'auto'`** (default): The default option to cache `fetch` requests before dynamic functions with the `cache` option they provide and not cache `fetch` requests after dynamic functions. -- **`'default-cache'`**: Allow any `cache` option to be passed to `fetch` but if no option is provided then set the `cache` option to `'force-cache'`. This means that even `fetch` requests after dynamic functions are considered static. +- **`'auto'`** (default): The default option to cache `fetch` requests before Dynamic APIs with the `cache` option they provide and not cache `fetch` requests after Dynamic APIs. +- **`'default-cache'`**: Allow any `cache` option to be passed to `fetch` but if no option is provided then set the `cache` option to `'force-cache'`. This means that even `fetch` requests after Dynamic APIs are considered static. - **`'only-cache'`**: Ensure all `fetch` requests opt into caching by changing the default to `cache: 'force-cache'` if no option is provided and causing an error if any `fetch` requests use `cache: 'no-store'`. - **`'force-cache'`**: Ensure all `fetch` requests opt into caching by setting the `cache` option of all `fetch` requests to `'force-cache'`. -- **`'default-no-store'`**: Allow any `cache` option to be passed to `fetch` but if no option is provided then set the `cache` option to `'no-store'`. This means that even `fetch` requests before dynamic functions are considered dynamic. +- **`'default-no-store'`**: Allow any `cache` option to be passed to `fetch` but if no option is provided then set the `cache` option to `'no-store'`. This means that even `fetch` requests before Dynamic APIs are considered dynamic. - **`'only-no-store'`**: Ensure all `fetch` requests opt out of caching by changing the default to `cache: 'no-store'` if no option is provided and causing an error if any `fetch` requests use `cache: 'force-cache'` - **`'force-no-store'`**: Ensure all `fetch` requests opt out of caching by setting the `cache` option of all `fetch` requests to `'no-store'`. This forces all `fetch` requests to be re-fetched every request even if they provide a `'force-cache'` option. diff --git a/docs/02-app/02-api-reference/04-functions/after.mdx b/docs/02-app/02-api-reference/04-functions/after.mdx index d0600fe228c72..d40307db85d1a 100644 --- a/docs/02-app/02-api-reference/04-functions/after.mdx +++ b/docs/02-app/02-api-reference/04-functions/after.mdx @@ -39,7 +39,7 @@ export default function Layout({ children }) { > **Good to know**: > > - `after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called. -> - `after` is a [dynamic function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. +> - `after` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. > - You can use React `cache` to deduplicate functions called inside `after`. > - [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `after` since the response has already been sent. > - `after` can be nested inside other `after` calls. diff --git a/docs/02-app/02-api-reference/04-functions/connection.mdx b/docs/02-app/02-api-reference/04-functions/connection.mdx index 5567bf34ab206..878bf293826eb 100644 --- a/docs/02-app/02-api-reference/04-functions/connection.mdx +++ b/docs/02-app/02-api-reference/04-functions/connection.mdx @@ -6,7 +6,7 @@ version: RC The `connection()` function allows you to indicate rendering should wait for an incoming user request before continuing. -It's useful when a component doesn’t use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-functions), but you want it to be dynamically rendered at runtime and not statically rendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `new Date()`. +It's useful when a component doesn’t use [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-apis), but you want it to be dynamically rendered at runtime and not statically rendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `new Date()`. ```ts filename="app/page.tsx" switcher import { connection } from 'next/server' diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index 7678c8468485f..6ac7294db0df0 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -66,7 +66,7 @@ To learn more about these options, see the [MDN docs](https://developer.mozilla. - `cookies` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access cookies. - In version 14 and earlier, `cookies` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. -- `cookies` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#dynamic-functions) whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). +- `cookies` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) whose returned values cannot be known ahead of time. Using it in a layout or page will opt a route into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering). - The `.delete` method can only be called: - In a [Server Action](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) or [Route Handler](/docs/app/building-your-application/routing/route-handlers). - If it belongs to the same domain from which `.set` is called. Additionally, the code must be executed on the same protocol (HTTP or HTTPS) as the cookie you want to delete. diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index c11396c522b49..b82df9a320b5f 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -45,7 +45,7 @@ export default async function Page() { - `headers` is an **asynchronous** function that returns a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function. - In version 14 and earlier, `headers` was a synchronous function. To help with backwards compatability, you can still access it synchronoulsy in Next.js 15, but this behavior will be deprecated in the future. - Since `headers` is read-only, you cannot `set` or `delete` the outgoing request headers. -- `headers` is a [Dynamic Function](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-functions) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)**. +- `headers` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies#dynamic-apis) whose returned values cannot be known ahead of time. Using it in will opt a route into **[dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-rendering)**. ## Examples diff --git a/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx b/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx index dad4a2c3e65cf..38d27ecbc891b 100644 --- a/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx +++ b/docs/02-app/02-api-reference/04-functions/unstable_rethrow.mdx @@ -49,7 +49,7 @@ The following Next.js APIs rely on throwing an error which should be rethrown an - [`redirect()`](/docs/app/building-your-application/routing/redirecting#redirect-function) - [`permanentRedirect()`](/docs/app/building-your-application/routing/redirecting#permanentredirect-function) -If a route segment is marked to throw an error unless it's static, a dynamic function call will also throw an error that should similarly not be caught by the developer. Note that Partial Prerendering (PPR) affects this behavior as well. These APIs are: +If a route segment is marked to throw an error unless it's static, a Dynamic API call will also throw an error that should similarly not be caught by the developer. Note that Partial Prerendering (PPR) affects this behavior as well. These APIs are: - [`cookies`](/docs/app/api-reference/functions/cookies) - [`headers`](/docs/app/api-reference/functions/headers) diff --git a/docs/02-app/02-api-reference/04-functions/use-router.mdx b/docs/02-app/02-api-reference/04-functions/use-router.mdx index 69b34c32068c4..47137bb9f31bb 100644 --- a/docs/02-app/02-api-reference/04-functions/use-router.mdx +++ b/docs/02-app/02-api-reference/04-functions/use-router.mdx @@ -51,7 +51,7 @@ export default function Page() { > **Good to know**: > > - The `<Link>` component automatically prefetch routes as they become visible in the viewport. -> - `refresh()` could re-produce the same result if fetch requests are cached. Other dynamic functions like `cookies` and `headers` could also change the response. +> - `refresh()` could re-produce the same result if fetch requests are cached. Other Dynamic APIs like `cookies` and `headers` could also change the response. ### Migrating from `next/router` diff --git a/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx b/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx index ef2b5febc8bb2..87a4ef565361b 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/devIndicators.mdx @@ -104,7 +104,7 @@ Route (app) Size First Load JS There are two reasons a route might opt out of static rendering: -- The presence of [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-functions) which rely on runtime information. +- The presence of [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-apis) which rely on runtime information. - An [uncached data request](/docs/app/building-your-application/data-fetching/fetching), like a call to an ORM or database driver. Check your route for any of these conditions, and if you are not able to statically render the route, then consider using [`loading.js`](/docs/app/api-reference/file-conventions/loading) or [`<Suspense />`](https://react.dev/reference/react/Suspense) to leverage [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming#what-is-streaming). From ad64dbc5e7867f74e018ebbf3409700a08fbf169 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 14:07:37 +0100 Subject: [PATCH 33/58] Fix broken links --- errors/app-static-to-dynamic-error.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/errors/app-static-to-dynamic-error.mdx b/errors/app-static-to-dynamic-error.mdx index 07e090ff34fa7..ece1895f78fae 100644 --- a/errors/app-static-to-dynamic-error.mdx +++ b/errors/app-static-to-dynamic-error.mdx @@ -5,7 +5,7 @@ description: This document explains the "app/ Static to Dynamic Error" in Next.j ## Why This Error Occurred -The "`app/` Static to Dynamic Error" happens when one of your routes in the `app/` directory is initially generated statically at build time, but during runtime it attempts to use dynamic server values (such as `cookies()` or `headers()`) either for a fallback path or while a path is being revalidated. +The "`app/` Static to Dynamic Error" happens when one of your routes in the `app/` directory is initially generated statically at build time, but during runtime it attempts to use dynamic server values (such as `cookies` or `headers`) either for a fallback path or while a path is being revalidated. Currently, Next.js does not support switching between static and dynamic types during runtime, so the system throws an error. @@ -20,4 +20,4 @@ To resolve this issue, you have two main options: ## Useful Links - [Static and Dynamic Rendering](/docs/app/building-your-application/rendering/server-components#server-rendering-strategies) - Learn more about the differences between static and dynamic rendering in Next.js. -- [Dynamic Functions](/docs/app/building-your-application/rendering/server-components#dynamic-functions) - Understand more about the usage of dynamic server functions in your Next.js application. +- [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-apis) - Understand more about the usage of dynamic server functions in your Next.js application. From 4170ae9ffcb77bc7939e429a5088f92df787454c Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:13:10 +0100 Subject: [PATCH 34/58] Update docs/02-app/02-api-reference/05-next-config-js/turbo.mdx Co-authored-by: Rich Haines <hello@richardhaines.dev> --- docs/02-app/02-api-reference/05-next-config-js/turbo.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx index 3d98ddc9e94e7..07e5cc55cd966 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx @@ -5,7 +5,7 @@ description: Configure Next.js with Turbopack-specific options {/* The content of this doc is shared between the app and pages router. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} -The `turbo` option allows you customize [Turbopack](/docs/architecture/turbopack) to transform different files and change how modules are resolved. +The `turbo` option lets you customize [Turbopack](/docs/architecture/turbopack) to transform different files and change how modules are resolved. ```ts filename="next.config.ts" switcher import type { NextConfig } from 'next' From ee14a3045bed89f0e9ae84c37a0c7cafeac0c0a3 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:13:18 +0100 Subject: [PATCH 35/58] Update docs/02-app/02-api-reference/05-next-config-js/turbo.mdx Co-authored-by: Rich Haines <hello@richardhaines.dev> --- docs/02-app/02-api-reference/05-next-config-js/turbo.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx index 07e5cc55cd966..6017aaa95e784 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/turbo.mdx @@ -32,7 +32,7 @@ module.exports = nextConfig > **Good to know**: > -> - Turbopack for Next.js does not require loaders nor loader configuration for built-in functionality. Turbopack has built-in support for css and compiling modern JavaScript, so there's no need for `css-loader`, `postcss-loader`, or `babel-loader` if you're using `@babel/preset-env`. +> - Turbopack for Next.js does not require loaders nor loader configuration for built-in functionality. Turbopack has built-in support for CSS and compiling modern JavaScript, so there's no need for `css-loader`, `postcss-loader`, or `babel-loader` if you're using `@babel/preset-env`. ## Reference From 5fd7864ee30d0f76cba79670eceb225266705a1b Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 14:13:33 +0100 Subject: [PATCH 36/58] Update after.mdx --- .../02-api-reference/04-functions/after.mdx | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/02-app/02-api-reference/04-functions/after.mdx b/docs/02-app/02-api-reference/04-functions/after.mdx index d40307db85d1a..3ec9c726fae04 100644 --- a/docs/02-app/02-api-reference/04-functions/after.mdx +++ b/docs/02-app/02-api-reference/04-functions/after.mdx @@ -4,7 +4,7 @@ description: API Reference for the after function. version: RC --- -`after()` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. +`after` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. It can be used in [Server Components](/docs/app/building-your-application/rendering/server-components) (including [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata)), [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Middleware](/docs/app/building-your-application/routing/middleware). @@ -12,6 +12,7 @@ The function accepts a callback that will be executed after the response is fini ```tsx filename="app/layout.tsx switcher import { after } from 'next/server' +// Custom logging function import { log } from '@/app/utils' export default function Layout({ children }: { children: React.ReactNode }) { @@ -25,6 +26,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { ```jsx filename="app/layout.js switcher import { after } from 'next/server' +// Custom logging function import { log } from '@/app/utils' export default function Layout({ children }) { @@ -36,34 +38,32 @@ export default function Layout({ children }) { } ``` -> **Good to know**: -> -> - `after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called. -> - `after` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. -> - You can use React `cache` to deduplicate functions called inside `after`. -> - [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `after` since the response has already been sent. -> - `after` can be nested inside other `after` calls. +## Reference -## Parameters +### Parameters - A callback function which will be executed after the response is finished. -## Returns +### Serverless function duration -- `after()` does not return a value. +`after` will run for the platform's default or configured max duration of a serverless function. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config#maxduration) route segment config. -## Alternatives +## Good to know -The use case for `after()` is to process secondary tasks without blocking the primary response. It's similar to using the platform's [`waitUntil()`](https://vercel.com/docs/functions/functions-api-reference) or removing `await` from a promise, but with the following differences: +- `after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called. +- `after` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. +- You can use React `cache` to deduplicate functions called inside `after`. +- [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `after` since the response has already been sent. +- `after` can be nested inside other `after` calls. -- **`waitUntil()`**: accepts a promise and enqueues a task to be executed during the lifecycle of the request, whereas `after()` accepts a callback that will be executed **after** the response is finished. -- **Removing `await`**: starts executing during the response, which uses resources. It's also not reliable in serverless environments as the function stops computation immediately after the response is sent, potentially interrupting the task. +## Alternatives -We recommend using `after()` as it has been designed to consider other Next.js APIs and contexts. +The use case for `after` is to process secondary tasks without blocking the primary response. It's similar to using the platform's [`waitUntil()`](https://vercel.com/docs/functions/functions-api-reference) or removing `await` from a promise, but with the following differences: -## Serverless function duration +- **`waitUntil()`**: accepts a promise and enqueues a task to be executed during the lifecycle of the request, whereas `after` accepts a callback that will be executed **after** the response is finished. +- **Removing `await`**: starts executing during the response, which uses resources. It's also not reliable in serverless environments as the function stops computation immediately after the response is sent, potentially interrupting the task. -`after()` will run for the platform's default or configured max duration of a serverless function. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config#maxduration) route segment config. +We recommend using `after` as it has been designed to consider other Next.js APIs and contexts. | Version History | Description | | --------------- | --------------------------------------------------------------------- | From d31144fb309088959cb6821fbce504ce4a889ec9 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 14:20:13 +0100 Subject: [PATCH 37/58] Expand on nested `after` functions --- docs/02-app/02-api-reference/04-functions/after.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/02-api-reference/04-functions/after.mdx b/docs/02-app/02-api-reference/04-functions/after.mdx index 3ec9c726fae04..63b08b764b2f5 100644 --- a/docs/02-app/02-api-reference/04-functions/after.mdx +++ b/docs/02-app/02-api-reference/04-functions/after.mdx @@ -54,7 +54,7 @@ export default function Layout({ children }) { - `after` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. - You can use React `cache` to deduplicate functions called inside `after`. - [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `after` since the response has already been sent. -- `after` can be nested inside other `after` calls. +- `after` can be nested inside other `after` calls, for example, you can create utility functions that wrap `after` calls to add additional functionality. ## Alternatives From 87f5f29ab1ded2ca2da790e953750506a78da7c3 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Wed, 18 Sep 2024 14:21:27 +0100 Subject: [PATCH 38/58] Add codemod heading to fix broken link --- .../11-upgrading/01-codemods.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx b/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx index a6166fb69b5bd..f1e8b6a993a76 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx @@ -22,7 +22,11 @@ Replacing `<transform>` and `<path>` with appropriate values. - `--dry` Do a dry-run, no code will be edited - `--print` Prints the changed output for comparison -## Next.js Codemods +## Codemods + +### 15.0 + +TODO ### 14.0 From 61465eae8c2ad26664b7ead553ac835fa0f7a28c Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Tue, 1 Oct 2024 10:07:55 +0100 Subject: [PATCH 39/58] Mark after as unstable - Reverting previous changes as after won't be stable for RC 2 --- .../02-api-reference/04-functions/after.mdx | 70 ------------------- .../04-functions/unstable_after.mdx | 70 +++++++++++++++++++ 2 files changed, 70 insertions(+), 70 deletions(-) delete mode 100644 docs/02-app/02-api-reference/04-functions/after.mdx create mode 100644 docs/02-app/02-api-reference/04-functions/unstable_after.mdx diff --git a/docs/02-app/02-api-reference/04-functions/after.mdx b/docs/02-app/02-api-reference/04-functions/after.mdx deleted file mode 100644 index 63b08b764b2f5..0000000000000 --- a/docs/02-app/02-api-reference/04-functions/after.mdx +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: after -description: API Reference for the after function. -version: RC ---- - -`after` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. - -It can be used in [Server Components](/docs/app/building-your-application/rendering/server-components) (including [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata)), [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Middleware](/docs/app/building-your-application/routing/middleware). - -The function accepts a callback that will be executed after the response is finished: - -```tsx filename="app/layout.tsx switcher -import { after } from 'next/server' -// Custom logging function -import { log } from '@/app/utils' - -export default function Layout({ children }: { children: React.ReactNode }) { - after(() => { - // Execute after the layout is rendered and sent to the user - log() - }) - return <>{children}</> -} -``` - -```jsx filename="app/layout.js switcher -import { after } from 'next/server' -// Custom logging function -import { log } from '@/app/utils' - -export default function Layout({ children }) { - after(() => { - // Execute after the layout is rendered and sent to the user - log() - }) - return <>{children}</> -} -``` - -## Reference - -### Parameters - -- A callback function which will be executed after the response is finished. - -### Serverless function duration - -`after` will run for the platform's default or configured max duration of a serverless function. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config#maxduration) route segment config. - -## Good to know - -- `after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called. -- `after` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. -- You can use React `cache` to deduplicate functions called inside `after`. -- [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `after` since the response has already been sent. -- `after` can be nested inside other `after` calls, for example, you can create utility functions that wrap `after` calls to add additional functionality. - -## Alternatives - -The use case for `after` is to process secondary tasks without blocking the primary response. It's similar to using the platform's [`waitUntil()`](https://vercel.com/docs/functions/functions-api-reference) or removing `await` from a promise, but with the following differences: - -- **`waitUntil()`**: accepts a promise and enqueues a task to be executed during the lifecycle of the request, whereas `after` accepts a callback that will be executed **after** the response is finished. -- **Removing `await`**: starts executing during the response, which uses resources. It's also not reliable in serverless environments as the function stops computation immediately after the response is sent, potentially interrupting the task. - -We recommend using `after` as it has been designed to consider other Next.js APIs and contexts. - -| Version History | Description | -| --------------- | --------------------------------------------------------------------- | -| `v15.0.0-rc` | `unstable_after` introduced, renamed to `after` and marked as stable. | diff --git a/docs/02-app/02-api-reference/04-functions/unstable_after.mdx b/docs/02-app/02-api-reference/04-functions/unstable_after.mdx new file mode 100644 index 0000000000000..b26f4c9656050 --- /dev/null +++ b/docs/02-app/02-api-reference/04-functions/unstable_after.mdx @@ -0,0 +1,70 @@ +--- +title: unstable_after +description: API Reference for the after function. +version: RC +--- + +`unstable_after` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. + +It can be used in [Server Components](/docs/app/building-your-application/rendering/server-components) (including [`generateMetadata`](https://nextjs.org/docs/app/api-reference/functions/generate-metadata)), [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations), [Route Handlers](/docs/app/building-your-application/routing/route-handlers), and [Middleware](/docs/app/building-your-application/routing/middleware). + +The function accepts a callback that will be executed after the response is finished: + +```tsx filename="app/layout.tsx switcher +import { unstable_after as after } from 'next/server' +// Custom logging function +import { log } from '@/app/utils' + +export default function Layout({ children }: { children: React.ReactNode }) { + after(() => { + // Execute after the layout is rendered and sent to the user + log() + }) + return <>{children}</> +} +``` + +```jsx filename="app/layout.js switcher +import { unstable_after as after } from 'next/server' +// Custom logging function +import { log } from '@/app/utils' + +export default function Layout({ children }) { + after(() => { + // Execute after the layout is rendered and sent to the user + log() + }) + return <>{children}</> +} +``` + +## Reference + +### Parameters + +- A callback function which will be executed after the response is finished. + +### Serverless function duration + +`unstable_after` will run for the platform's default or configured max duration of a serverless function. If your platform supports it, you can configure the timeout limit using the [`maxDuration`](/docs/app/api-reference/file-conventions/route-segment-config#maxduration) route segment config. + +## Good to know + +- `unstable_after` will be executed even if the response didn't complete successfully. Including when an error is thrown or when `notFound` or `redirect` is called. +- `unstable_after` is a [Dynamic API](/docs/app/building-your-application/rendering/server-components#dynamic-apis) that will opt a route into dynamic rendering. This behavior can be overridden with the [`export dynamic = "force-static"`](/docs/app/api-reference/file-conventions/route-segment-config#dynamic) segment config. +- You can use React `cache` to deduplicate functions called inside `unstable_after`. +- [`cookies`](/docs/app/api-reference/functions/cookies) cannot be set inside `unstable_after` since the response has already been sent. +- `unstable_after` can be nested inside other `unstable_after` calls, for example, you can create utility functions that wrap `unstable_after` calls to add additional functionality. + +## Alternatives + +The use case for `unstable_after` is to process secondary tasks without blocking the primary response. It's similar to using the platform's [`waitUntil()`](https://vercel.com/docs/functions/functions-api-reference) or removing `await` from a promise, but with the following differences: + +- **`waitUntil()`**: accepts a promise and enqueues a task to be executed during the lifecycle of the request, whereas `unstable_after` accepts a callback that will be executed **after** the response is finished. +- **Removing `await`**: starts executing during the response, which uses resources. It's also not reliable in serverless environments as the function stops computation immediately after the response is sent, potentially interrupting the task. + +We recommend using `unstable_after` as it has been designed to consider other Next.js APIs and contexts. + +| Version History | Description | +| --------------- | ---------------------------- | +| `v15.0.0-rc` | `unstable_after` introduced. | From d34d14815539a74967a1ad85267b31310cbb7d93 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:10:34 +0100 Subject: [PATCH 40/58] Apply suggestions from code review Co-authored-by: Ahmed Abdelbaset <A7med3bdulBaset@gmail.com> --- .../03-server-actions-and-mutations.mdx | 12 ++++++------ .../03-rendering/04-partial-prerendering.mdx | 8 ++++---- .../07-configuring/11-draft-mode.mdx | 8 ++++---- .../07-configuring/15-content-security-policy.mdx | 4 ++-- .../09-authentication/index.mdx | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) 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 e69af30696346..9ef47693b649b 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 @@ -839,13 +839,13 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - await cookies().get('name')?.value + (await cookies()).get('name')?.value // Set cookie - await cookies().set('name', 'Delba') + (await cookies()).set('name', 'Delba') // Delete cookie - await cookies().delete('name') + (await cookies()).delete('name') } ``` @@ -856,13 +856,13 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - await cookies().get('name')?.value + (await cookies()).get('name')?.value // Set cookie - await cookies().set('name', 'Delba') + (await cookies()).set('name', 'Delba') // Delete cookie - await cookies().delete('name') + (await cookies()).delete('name') } ``` 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 aaa6014865c29..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 @@ -135,7 +135,7 @@ For example, using functions like `cookies` or `headers`: import { cookies } from 'next/headers' export async function User() { - const session = await cookies().get('session')?.value + const session = (await cookies()).get('session')?.value return '...' } ``` @@ -144,7 +144,7 @@ export async function User() { import { cookies } from 'next/headers' export async function User() { - const session = await cookies().get('session')?.value + const session = (await cookies()).get('session')?.value return '...' } ``` @@ -227,7 +227,7 @@ Inside of the table component, accessing the value from `searchParams` will make export async function Table({ searchParams, }: { - searchParams: { sort: string } + searchParams: Promise<{ sort: string }> }) { const sort = (await searchParams).sort === 'true' return '...' @@ -236,7 +236,7 @@ export async function Table({ ```jsx filename="app/table.js" switcher export async function Table({ searchParams }: { - searchParams: { sort: string } + searchParams: Promise<{ sort: string }> }) { const sort = (await searchParams).sort === 'true'; return '...' 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 9e664be6dbfee..133dc50baec2e 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,7 @@ Then, import the [`draftMode`](/docs/app/api-reference/functions/draft-mode) fun import { draftMode } from 'next/headers' export async function GET(request: Request) { - await draftMode().enable() + (await draftMode()).enable() return new Response('Draft mode is enabled') } ``` @@ -43,7 +43,7 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - await draftMode().enable() + (await draftMode()).enable() return new Response('Draft mode is enabled') } ``` @@ -99,7 +99,7 @@ export async function GET(request: Request) { } // Enable Draft Mode by setting the cookie - await draftMode().enable() + (await draftMode()).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 +133,7 @@ export async function GET(request) { } // Enable Draft Mode by setting the cookie - await draftMode().enable() + (await draftMode()).enable() // Redirect to the path from the fetched post // We don't redirect to searchParams.slug as that might lead to open redirect vulnerabilities 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 6c7dabd8adfae..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 @@ -178,7 +178,7 @@ import { headers } from 'next/headers' import Script from 'next/script' export default async function Page() { - const nonce = await headers().get('x-nonce') + const nonce = (await headers()).get('x-nonce') return ( <Script @@ -195,7 +195,7 @@ import { headers } from 'next/headers' import Script from 'next/script' export default async function Page() { - const nonce = await headers().get('x-nonce') + const nonce = (await headers()).get('x-nonce') return ( <Script diff --git a/docs/02-app/01-building-your-application/09-authentication/index.mdx b/docs/02-app/01-building-your-application/09-authentication/index.mdx index cb75bd3125453..8594ad301e45d 100644 --- a/docs/02-app/01-building-your-application/09-authentication/index.mdx +++ b/docs/02-app/01-building-your-application/09-authentication/index.mdx @@ -659,7 +659,7 @@ export async function createSession(userId: string) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) const session = await encrypt({ userId, expiresAt }) - await cookies().set('session', session, { + (await cookies()).set('session', session, { httpOnly: true, secure: true, expires: expiresAt, From 39422ba03038c9de381d64e2bddb3aa973cceffb Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Tue, 1 Oct 2024 10:11:41 +0100 Subject: [PATCH 41/58] Apply feedback --- .../06-optimizing/04-metadata.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 48d510f9ffdb7..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( From d26d76d0ce701c2b9e41727e67970ddae1291989 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 07:59:34 +0100 Subject: [PATCH 42/58] Apply suggestions from code review Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com> --- .../09-authentication/index.mdx | 22 ++++++++-------- .../10-deploying/index.mdx | 2 +- .../11-upgrading/04-app-router-migration.mdx | 8 +++--- .../02-api-reference/04-functions/cookies.mdx | 25 ++++++++++--------- .../04-functions/draft-mode.mdx | 8 +++--- .../02-api-reference/04-functions/headers.mdx | 2 +- 6 files changed, 34 insertions(+), 33 deletions(-) diff --git a/docs/02-app/01-building-your-application/09-authentication/index.mdx b/docs/02-app/01-building-your-application/09-authentication/index.mdx index 8594ad301e45d..c1deb71d1b232 100644 --- a/docs/02-app/01-building-your-application/09-authentication/index.mdx +++ b/docs/02-app/01-building-your-application/09-authentication/index.mdx @@ -738,7 +738,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export async function updateSession() { - const session = await cookies().get('session')?.value + const session = (await cookies()).get('session')?.value const payload = await decrypt(session) if (!session || !payload) { @@ -746,7 +746,7 @@ export async function updateSession() { } const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - await cookies().set('session', session, { + (await cookies()).set('session', session, { httpOnly: true, secure: true, expires: expires, @@ -762,7 +762,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export async function updateSession() { - const session = await cookies().get('session').value + const session = (await cookies()).get('session')?.value const payload = await decrypt(session) if (!session || !payload) { @@ -770,7 +770,7 @@ export async function updateSession() { } const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - await cookies().set('session', session, { + (await cookies()).set('session', session, { httpOnly: true, secure: true, expires: expires, @@ -791,7 +791,7 @@ import 'server-only' import { cookies } from 'next/headers' export async function deleteSession() { - await cookies().delete('session') + (await cookies()).delete('session') } ``` @@ -800,7 +800,7 @@ import 'server-only' import { cookies } from 'next/headers' export async function deleteSession() { - await cookies().delete('session') + (await cookies()).delete('session') } ``` @@ -945,7 +945,7 @@ export async function createSession(id) { const session = await encrypt({ sessionId, expiresAt }) // 3. Store the session in cookies for optimistic auth checks - await cookies().set('session', session, { + (await cookies()).set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -1056,7 +1056,7 @@ export default async function middleware(req: NextRequest) { const isPublicRoute = publicRoutes.includes(path) // 3. Decrypt the session from the cookie - const cookie = await cookies().get('session')?.value + const cookie = (await cookies()).get('session')?.value const session = await decrypt(cookie) // 4. Redirect to /login if the user is not authenticated @@ -1098,7 +1098,7 @@ export default async function middleware(req) { const isPublicRoute = publicRoutes.includes(path) // 3. Decrypt the session from the cookie - const cookie = await cookies().get('session')?.value + const cookie = (await cookies()).get('session')?.value const session = await decrypt(cookie) // 5. Redirect to /login if the user is not authenticated @@ -1149,7 +1149,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export const verifySession = cache(async () => { - const cookie = await cookies().get('session')?.value + const cookie = (await cookies()).get('session')?.value const session = await decrypt(cookie) if (!session?.userId) { @@ -1167,7 +1167,7 @@ import { cookies } from 'next/headers' import { decrypt } from '@/app/lib/session' export const verifySession = cache(async () => { - const cookie = await cookies().get('session').value + const cookie = (await cookies()).get('session')?.value const session = await decrypt(cookie) if (!session.userId) { diff --git a/docs/02-app/01-building-your-application/10-deploying/index.mdx b/docs/02-app/01-building-your-application/10-deploying/index.mdx index adc4e3b8ebfdf..6d2165cbdd2c5 100644 --- a/docs/02-app/01-building-your-application/10-deploying/index.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/index.mdx @@ -124,7 +124,7 @@ export default function Component() { ```jsx filename="app/page.js" switcher import { connection } from 'next/server' -export default function Component() { +export default async function Component() { await connection() // cookies, headers, and other Dynamic APIs // will also opt into dynamic rendering, meaning diff --git a/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx b/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx index 38ad9b3b5876d..cb8014e589dc7 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/04-app-router-migration.mdx @@ -637,7 +637,7 @@ The `app` directory exposes new read-only functions to retrieve request data: import { cookies, headers } from 'next/headers' async function getData() { - const authHeader = await headers().get('authorization') + const authHeader = (await headers()).get('authorization') return '...' } @@ -645,7 +645,7 @@ async function getData() { export default async function Page() { // You can use `cookies` or `headers` inside Server Components // directly or in your data fetching function - const theme = await cookies().get('theme') + const theme = (await cookies()).get('theme') const data = await getData() return '...' } @@ -656,7 +656,7 @@ export default async function Page() { import { cookies, headers } from 'next/headers' async function getData() { - const authHeader = await headers().get('authorization') + const authHeader = (await headers()).get('authorization') return '...' } @@ -664,7 +664,7 @@ async function getData() { export default async function Page() { // You can use `cookies` or `headers` inside Server Components // directly or in your data fetching function - const theme = await cookies().get('theme') + const theme = (await cookies()).get('theme') const data = await getData() return '...' } diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index 6ac7294db0df0..cf5c4b4e10815 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -140,11 +140,11 @@ You can use the `cookies().set(name, value, options)` method in a [Server Action import { cookies } from 'next/headers' export async function create(data) { - await cookies().set('name', 'lee') + (await cookies()).set('name', 'lee') // or - await cookies().set('name', 'lee', { secure: true }) + (await cookies()).set('name', 'lee', { secure: true }) // or - await cookies().set({ + (await cookies()).set({ name: 'name', value: 'lee', httpOnly: true, @@ -159,11 +159,11 @@ export async function create(data) { import { cookies } from 'next/headers' export async function create(data) { - await cookies().set('name', 'lee') + (await cookies()).set('name', 'lee') // or - await cookies().set('name', 'lee', { secure: true }) + (await cookies()).set('name', 'lee', { secure: true }) // or - await cookies().set({ + (await cookies()).set({ name: 'name', value: 'lee', httpOnly: true, @@ -208,7 +208,7 @@ Using the `delete()` method: import { cookies } from 'next/headers' export async function delete(data) { - await cookies().delete('name') + (await cookies()).delete('name') } ``` @@ -218,7 +218,7 @@ export async function delete(data) { import { cookies } from 'next/headers' export async function delete(data) { - await cookies().delete('name') + (await cookies()).delete('name') } ``` @@ -230,7 +230,7 @@ Setting a new cookie with the same name and an empty value: import { cookies } from 'next/headers' export async function delete(data) { - await cookies().set('name', '') + (await cookies()).set('name', '') } ``` @@ -240,7 +240,7 @@ export async function delete(data) { import { cookies } from 'next/headers' export async function delete(data) { - await cookies().set('name', '') + (await cookies()).set('name', '') } ``` @@ -252,7 +252,7 @@ Setting the `maxAge` to 0 will immediately expire a cookie. `maxAge` accepts a v import { cookies } from 'next/headers' export async function delete(data) { - await cookies().set('name', 'value', { maxAge: 0 }) + (await cookies()).set('name', 'value', { maxAge: 0 }) } ``` @@ -262,7 +262,8 @@ export async function delete(data) { import { cookies } from 'next/headers' export async function delete(data) { - await cookies().set('name', 'value', { maxAge: 0 }) + (await cookies()).set('name', 'value', { maxAge: 0 }) +`` } ``` diff --git a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx index fb11ca0060c46..3b7082fea1a3f 100644 --- a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx +++ b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx @@ -53,7 +53,7 @@ To enable Draft Mode, create a new [Route Handler](/docs/app/building-your-appli import { draftMode } from 'next/headers' export async function GET(request: Request) { - await draftMode().enable() + (await draftMode()).enable() return new Response('Draft mode is enabled') } ``` @@ -62,7 +62,7 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - await draftMode().enable() + (await draftMode()).enable() return new Response('Draft mode is enabled') } ``` @@ -77,7 +77,7 @@ To disable Draft Mode manually, call the `disable()` method in your [Route Handl import { draftMode } from 'next/headers' export async function GET(request: Request) { - await draftMode().disable() + (await draftMode()).disable() return new Response('Draft mode is disabled') } ``` @@ -86,7 +86,7 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - await draftMode().disable() + (await draftMode()).disable() return new Response('Draft mode is disabled') } ``` diff --git a/docs/02-app/02-api-reference/04-functions/headers.mdx b/docs/02-app/02-api-reference/04-functions/headers.mdx index b82df9a320b5f..95ea99742cbf8 100644 --- a/docs/02-app/02-api-reference/04-functions/headers.mdx +++ b/docs/02-app/02-api-reference/04-functions/headers.mdx @@ -55,7 +55,7 @@ export default async function Page() { import { headers } from 'next/headers' export default async function Page() { - const authorization = await headers().get('authorization') + const authorization = (await headers()).get('authorization') const res = await fetch('...', { headers: { authorization }, // Forward the authorization header }) From c2f14cddf65211a42214abcd6ac52663e7c9d048 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:00:24 +0100 Subject: [PATCH 43/58] Update docs/02-app/01-building-your-application/09-authentication/index.mdx Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com> --- .../01-building-your-application/09-authentication/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/01-building-your-application/09-authentication/index.mdx b/docs/02-app/01-building-your-application/09-authentication/index.mdx index c1deb71d1b232..a20cf725796b9 100644 --- a/docs/02-app/01-building-your-application/09-authentication/index.mdx +++ b/docs/02-app/01-building-your-application/09-authentication/index.mdx @@ -911,7 +911,7 @@ export async function createSession(id: number) { const session = await encrypt({ sessionId, expiresAt }) // 3. Store the session in cookies for optimistic auth checks - await cookies().set('session', session, { + (await cookies()).set('session', session, { httpOnly: true, secure: true, expires: expiresAt, From 7db3f1bbbd48ed88d31da19dd278ed920c58d0bf Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:00:34 +0100 Subject: [PATCH 44/58] Update docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com> --- .../07-configuring/03-environment-variables.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 13885b7046fa7..e45f4a4b56ff7 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 @@ -202,7 +202,7 @@ 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 function Component() { +export default async function Component() { await connection() // cookies, headers, and other Dynamic APIs // will also opt into dynamic rendering, meaning From 76fb8c8e9bfea3da8123bf071524bf460c3a135a Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:00:44 +0100 Subject: [PATCH 45/58] Update docs/02-app/01-building-your-application/09-authentication/index.mdx Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com> --- .../01-building-your-application/09-authentication/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/01-building-your-application/09-authentication/index.mdx b/docs/02-app/01-building-your-application/09-authentication/index.mdx index a20cf725796b9..c9ccbc0f4f5a2 100644 --- a/docs/02-app/01-building-your-application/09-authentication/index.mdx +++ b/docs/02-app/01-building-your-application/09-authentication/index.mdx @@ -677,7 +677,7 @@ export async function createSession(userId) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) const session = await encrypt({ userId, expiresAt }) - await cookies().set('session', session, { + (await cookies()).set('session', session, { httpOnly: true, secure: true, expires: expiresAt, From 3877eb2e15ba07b4773f36b445fcd74e4e5e7c38 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:00:56 +0100 Subject: [PATCH 46/58] Update docs/02-app/01-building-your-application/10-deploying/index.mdx Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com> --- docs/02-app/01-building-your-application/10-deploying/index.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/02-app/01-building-your-application/10-deploying/index.mdx b/docs/02-app/01-building-your-application/10-deploying/index.mdx index 6d2165cbdd2c5..8826ae08a4464 100644 --- a/docs/02-app/01-building-your-application/10-deploying/index.mdx +++ b/docs/02-app/01-building-your-application/10-deploying/index.mdx @@ -111,7 +111,7 @@ You safely read environment variables on the server during dynamic rendering. ```tsx filename="app/page.ts" switcher import { connection } from 'next/server' -export default function Component() { +export default async function Component() { await connection() // cookies, headers, and other Dynamic APIs // will also opt into dynamic rendering, meaning From 5c61517ac03a4030051e72253ad6817b80ba0bf0 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:01:33 +0100 Subject: [PATCH 47/58] Update docs/02-app/01-building-your-application/07-configuring/03-environment-variables.mdx Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com> --- .../07-configuring/03-environment-variables.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e45f4a4b56ff7..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 @@ -215,7 +215,7 @@ export default async function Component() { ```jsx filename="app/page.js" switcher import { connection } from 'next/server' -export default function Component() { +export default async function Component() { await connection() // cookies, headers, and other Dynamic APIs // will also opt into dynamic rendering, meaning From 194d15c66afe042ede3dc5b34144c40c68d499aa Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 08:28:14 +0100 Subject: [PATCH 48/58] Fix broken link --- errors/sync-dynamic-apis.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/sync-dynamic-apis.mdx b/errors/sync-dynamic-apis.mdx index a87c749fc56b6..ca9bc27083d5c 100644 --- a/errors/sync-dynamic-apis.mdx +++ b/errors/sync-dynamic-apis.mdx @@ -5,7 +5,7 @@ description: Learn more about why accessing certain APIs synchronously now warns ## Why This Warning Occurred -Somewhere in your code you used an API that opts into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-functions). +Somewhere in your code you used an API that opts into [dynamic rendering](/docs/app/building-your-application/rendering/server-components#dynamic-apis). Dynamic APIs are: From ff4c7dfec1d2de6ec68b4352a82d2ce394a04682 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 08:52:05 +0100 Subject: [PATCH 49/58] Fix borked await --- .../07-configuring/11-draft-mode.mdx | 12 +++-- .../09-authentication/index.mdx | 44 ++++++++++++------- .../02-api-reference/04-functions/cookies.mdx | 16 ++++--- .../04-functions/draft-mode.mdx | 16 +++---- 4 files changed, 53 insertions(+), 35 deletions(-) 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 6a474a0e462a9..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) { - (await 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) { - (await 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 - (await 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 - (await 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 diff --git a/docs/02-app/01-building-your-application/09-authentication/index.mdx b/docs/02-app/01-building-your-application/09-authentication/index.mdx index c6361e0c4ef4a..d044ccb4c3808 100644 --- a/docs/02-app/01-building-your-application/09-authentication/index.mdx +++ b/docs/02-app/01-building-your-application/09-authentication/index.mdx @@ -657,15 +657,17 @@ import { cookies } from 'next/headers' export async function createSession(userId: string) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - const session = await encrypt({ userId, expiresAt }) - - (await cookies()).set('session', session, { - httpOnly: true, - secure: true, - expires: expiresAt, - sameSite: 'lax', - path: '/', - }) + const session = await encrypt({ userId, expiresAt })(await cookies()).set( + 'session', + session, + { + httpOnly: true, + secure: true, + expires: expiresAt, + sameSite: 'lax', + path: '/', + } + ) } ``` @@ -676,8 +678,9 @@ import { cookies } from 'next/headers' export async function createSession(userId) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) const session = await encrypt({ userId, expiresAt }) + const cookieStore = await cookies() - (await cookies()).set('session', session, { + cookieStore().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -746,7 +749,9 @@ export async function updateSession() { } const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - (await cookies()).set('session', session, { + + const cookieStore = await cookies() + cookieStore().set('session', session, { httpOnly: true, secure: true, expires: expires, @@ -769,8 +774,9 @@ export async function updateSession() { return null } - const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) - (await cookies()).set('session', session, { + const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)( + await cookies() + ).set('session', session, { httpOnly: true, secure: true, expires: expires, @@ -791,7 +797,8 @@ import 'server-only' import { cookies } from 'next/headers' export async function deleteSession() { - (await cookies()).delete('session') + const cookieStore = await cookies() + cookieStore().delete('session') } ``` @@ -800,7 +807,8 @@ import 'server-only' import { cookies } from 'next/headers' export async function deleteSession() { - (await cookies()).delete('session') + const cookieStore = await cookies() + cookieStore().delete('session') } ``` @@ -911,7 +919,8 @@ export async function createSession(id: number) { const session = await encrypt({ sessionId, expiresAt }) // 3. Store the session in cookies for optimistic auth checks - (await cookies()).set('session', session, { + const cookieStore = await cookies() + cookieStore().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, @@ -945,7 +954,8 @@ export async function createSession(id) { const session = await encrypt({ sessionId, expiresAt }) // 3. Store the session in cookies for optimistic auth checks - (await cookies()).set('session', session, { + const cookieStore = await cookies() + cookieStore().set('session', session, { httpOnly: true, secure: true, expires: expiresAt, diff --git a/docs/02-app/02-api-reference/04-functions/cookies.mdx b/docs/02-app/02-api-reference/04-functions/cookies.mdx index cf5c4b4e10815..13762003552a9 100644 --- a/docs/02-app/02-api-reference/04-functions/cookies.mdx +++ b/docs/02-app/02-api-reference/04-functions/cookies.mdx @@ -140,11 +140,13 @@ You can use the `cookies().set(name, value, options)` method in a [Server Action import { cookies } from 'next/headers' export async function create(data) { - (await cookies()).set('name', 'lee') + const cookieStore = await cookies() + + cookieStore().set('name', 'lee') // or - (await cookies()).set('name', 'lee', { secure: true }) + cookieStore().set('name', 'lee', { secure: true }) // or - (await cookies()).set({ + cookieStore().set({ name: 'name', value: 'lee', httpOnly: true, @@ -159,11 +161,13 @@ export async function create(data) { import { cookies } from 'next/headers' export async function create(data) { - (await cookies()).set('name', 'lee') + const cookieStore = await cookies() + + cookieStore().set('name', 'lee') // or - (await cookies()).set('name', 'lee', { secure: true }) + cookieStore().set('name', 'lee', { secure: true }) // or - (await cookies()).set({ + cookieStore().set({ name: 'name', value: 'lee', httpOnly: true, diff --git a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx index 3b7082fea1a3f..ec6deee7fd780 100644 --- a/docs/02-app/02-api-reference/04-functions/draft-mode.mdx +++ b/docs/02-app/02-api-reference/04-functions/draft-mode.mdx @@ -53,7 +53,8 @@ To enable Draft Mode, create a new [Route Handler](/docs/app/building-your-appli import { draftMode } from 'next/headers' export async function GET(request: Request) { - (await draftMode()).enable() + const draft = await draftMode() + draft().enable() return new Response('Draft mode is enabled') } ``` @@ -62,7 +63,8 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - (await draftMode()).enable() + const draft = await draftMode() + draft().enable() return new Response('Draft mode is enabled') } ``` @@ -77,7 +79,8 @@ To disable Draft Mode manually, call the `disable()` method in your [Route Handl import { draftMode } from 'next/headers' export async function GET(request: Request) { - (await draftMode()).disable() + const draft = await draftMode() + draft().disable() return new Response('Draft mode is disabled') } ``` @@ -86,7 +89,8 @@ export async function GET(request: Request) { import { draftMode } from 'next/headers' export async function GET(request) { - (await draftMode()).disable() + const draft = await draftMode() + draft().disable() return new Response('Draft mode is disabled') } ``` @@ -131,7 +135,3 @@ export default async function Page() { | ------------ | --------------------------------------------------------------------------------------------------------------------------- | | `v15.0.0-RC` | `draftMode` is now an async function. A [codemod](/docs/app/building-your-application/upgrading/codemods#150) is available. | | `v13.4.0` | `draftMode` introduced. | - -``` - -``` From fed09f39881d88745c808d7e831bfa148ab89148 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 08:54:09 +0100 Subject: [PATCH 50/58] More fixes --- .../03-server-actions-and-mutations.mdx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) 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 9ef47693b649b..d022348aa8050 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 @@ -839,13 +839,13 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - (await cookies()).get('name')?.value + cookieStore.get('name')?.value // Set cookie - (await cookies()).set('name', 'Delba') + cookieStore.set('name', 'Delba') // Delete cookie - (await cookies()).delete('name') + cookieStore.delete('name') } ``` @@ -856,13 +856,16 @@ import { cookies } from 'next/headers' export async function exampleAction() { // Get cookie - (await cookies()).get('name')?.value + const cookieStore = await cookies() + + // Get cookie + cookieStore.get('name')?.value // Set cookie - (await cookies()).set('name', 'Delba') + cookieStore.set('name', 'Delba') // Delete cookie - (await cookies()).delete('name') + cookieStore.delete('name') } ``` From a7148d9c0c36fe72e2893babe9f7bc38ef5bb4b0 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 08:54:20 +0100 Subject: [PATCH 51/58] Update secure server actions section --- .../03-server-actions-and-mutations.mdx | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) 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 d022348aa8050..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,6 +838,8 @@ 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 cookieStore.get('name')?.value @@ -873,13 +875,32 @@ See [additional examples](/docs/app/api-reference/functions/cookies#deleting-coo ## Security -When you add the `"use server"` directive to the top of a file, it marks all the exports in that file as API endpoints. By default, exported but unused functions are tree-shaken. That is, Next.js evaluates your codebase and removes references to unused Server Actions from the client-side JavaScript bundle. +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: -In addition, the IDs that link the client-side reference to the server-side code is re-encrypted on each new deployment. This means Server Actions can only be invoked internally by the application. +- **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 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' From 8f8de82cf4c80959e7977e764327855da15e0beb Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 08:55:10 +0100 Subject: [PATCH 52/58] Clean up --- .../01-building-your-application/11-upgrading/01-codemods.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx b/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx index c84ba3db0a35c..2b9c7e4bf59fb 100644 --- a/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx +++ b/docs/02-app/01-building-your-application/11-upgrading/01-codemods.mdx @@ -26,10 +26,6 @@ Replacing `<transform>` and `<path>` with appropriate values. ### 15.0 -TODO - -### 15.0 - #### Transform App Router Route Segment Config `runtime` value from `experimental-edge` to `edge` ##### `app-dir-runtime-config-experimental-edge` From 6f1472d44af3782e1cc8589c1167cfc2c41982a1 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 08:59:46 +0100 Subject: [PATCH 53/58] Add new prefetch prop to next/form --- docs/02-app/02-api-reference/01-components/form.mdx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/02-app/02-api-reference/01-components/form.mdx b/docs/02-app/02-api-reference/01-components/form.mdx index 1fe3cfbcaf6e9..6f70aff14e169 100644 --- a/docs/02-app/02-api-reference/01-components/form.mdx +++ b/docs/02-app/02-api-reference/01-components/form.mdx @@ -53,16 +53,18 @@ The behavior of the `<Form>` component depends on whether the `action` prop is p When `action` is a string, the `<Form>` component supports the following props: -| Prop | Example | Type | Required | -| --------- | ------------------ | ------------------------------- | -------- | -| `action` | `action="/search"` | `string` (URL or relative path) | Yes | -| `replace` | `replace={false}` | `boolean` | - | -| `scroll` | `scroll={true}` | `boolean` | - | +| Prop | Example | Type | Required | +| ---------- | ------------------ | ------------------------------- | -------- | +| `action` | `action="/search"` | `string` (URL or relative path) | Yes | +| `replace` | `replace={false}` | `boolean` | - | +| `scroll` | `scroll={true}` | `boolean` | - | +| `prefetch` | `prefetch={false}` | `boolean` | - | - **`action`**: The URL or path to navigate to when the form is submitted. - An empty string `""` will navigate to the same route with updated search params. - **`scroll`**: Controls the scroll behavior during navigation. Defaults to `true`, this means it will scroll to the top of the new route, and maintain the scroll position for backwards and forwards navigation. - **`replace`**: Replaces the current history state instead of pushing a new one to the [browser's history](https://developer.mozilla.org/en-US/docs/Web/API/History_API) stack. Default is `false`. +- **`prefetch`**: Controls whether the path should be prefetched when the form becomes visible in the user's viewport. Defaults to `true`. ### `action` (function) Props From fbd8f736458b02fa7df3f3421f75c41ff18fb164 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 10:07:26 +0100 Subject: [PATCH 54/58] Add note about experimental_edge deprecation --- .../02-file-conventions/route-segment-config.mdx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx index 324a94a66d6eb..e15c739401bed 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx @@ -221,3 +221,9 @@ export const maxDuration = 5 The `generateStaticParams` function can be used in combination with [dynamic route segments](/docs/app/building-your-application/routing/dynamic-routes) to define the list of route segment parameters that will be statically generated at build time instead of on-demand at request time. See the [API reference](/docs/app/api-reference/functions/generate-static-params) for more details. + +## Version History + +| Version | | +| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `export const runtime = "experimental-edge"` deprecated. A [codemod](/docs/canary/app/building-your-application/upgrading/codemods#transform-app-router-route-segment-config-runtime-value-from-experimental-edge-to-edge) is available. | From 5f28cfb59f87fcb3ede75cb46446255a025d3b42 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 10:11:39 +0100 Subject: [PATCH 55/58] Update canary tags --- docs/02-app/02-api-reference/01-components/form.mdx | 5 ++--- docs/02-app/02-api-reference/04-functions/connection.mdx | 1 - docs/02-app/02-api-reference/04-functions/unstable_after.mdx | 1 - .../02-api-reference/05-next-config-js/reactCompiler.mdx | 2 +- .../05-next-config-js/serverComponentsHmrCache.mdx | 2 +- 5 files changed, 4 insertions(+), 7 deletions(-) diff --git a/docs/02-app/02-api-reference/01-components/form.mdx b/docs/02-app/02-api-reference/01-components/form.mdx index 6f70aff14e169..05d4e19019e2c 100644 --- a/docs/02-app/02-api-reference/01-components/form.mdx +++ b/docs/02-app/02-api-reference/01-components/form.mdx @@ -1,7 +1,6 @@ --- title: <Form> description: Learn how to use the `<Form>` component to handle form submissions and search params updates with client-side navigation. -version: canary --- The `<Form>` component extends the HTML `<form>` element to provide [**prefetching**](/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching) of [loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming), **client-side navigation** on submission, and **progressive enhancement**. @@ -58,12 +57,12 @@ When `action` is a string, the `<Form>` component supports the following props: | `action` | `action="/search"` | `string` (URL or relative path) | Yes | | `replace` | `replace={false}` | `boolean` | - | | `scroll` | `scroll={true}` | `boolean` | - | -| `prefetch` | `prefetch={false}` | `boolean` | - | +| `prefetch` | `prefetch={true}` | `boolean` | - | - **`action`**: The URL or path to navigate to when the form is submitted. - An empty string `""` will navigate to the same route with updated search params. -- **`scroll`**: Controls the scroll behavior during navigation. Defaults to `true`, this means it will scroll to the top of the new route, and maintain the scroll position for backwards and forwards navigation. - **`replace`**: Replaces the current history state instead of pushing a new one to the [browser's history](https://developer.mozilla.org/en-US/docs/Web/API/History_API) stack. Default is `false`. +- **`scroll`**: Controls the scroll behavior during navigation. Defaults to `true`, this means it will scroll to the top of the new route, and maintain the scroll position for backwards and forwards navigation. - **`prefetch`**: Controls whether the path should be prefetched when the form becomes visible in the user's viewport. Defaults to `true`. ### `action` (function) Props diff --git a/docs/02-app/02-api-reference/04-functions/connection.mdx b/docs/02-app/02-api-reference/04-functions/connection.mdx index 878bf293826eb..cf4114218264c 100644 --- a/docs/02-app/02-api-reference/04-functions/connection.mdx +++ b/docs/02-app/02-api-reference/04-functions/connection.mdx @@ -1,7 +1,6 @@ --- title: connection description: API Reference for the connection function. -version: RC --- The `connection()` function allows you to indicate rendering should wait for an incoming user request before continuing. diff --git a/docs/02-app/02-api-reference/04-functions/unstable_after.mdx b/docs/02-app/02-api-reference/04-functions/unstable_after.mdx index b26f4c9656050..43b32f6b900dc 100644 --- a/docs/02-app/02-api-reference/04-functions/unstable_after.mdx +++ b/docs/02-app/02-api-reference/04-functions/unstable_after.mdx @@ -1,7 +1,6 @@ --- title: unstable_after description: API Reference for the after function. -version: RC --- `unstable_after` allows you to schedule work to be executed after a response is finished. This is useful for tasks and other side effects that should not block the response, such as logging and analytics. diff --git a/docs/02-app/02-api-reference/05-next-config-js/reactCompiler.mdx b/docs/02-app/02-api-reference/05-next-config-js/reactCompiler.mdx index b6eea14158398..7c442070ced5f 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/reactCompiler.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/reactCompiler.mdx @@ -1,7 +1,7 @@ --- title: reactCompiler description: Enable the React Compiler to automatically optimize component rendering. -version: RC +version: experimental --- Next.js 15 RC introduced support for the [React Compiler](https://react.dev/learn/react-compiler). The compiler improves performance by automatically optimizing component rendering. This reduces the amount of manual memoization developers have to do through APIs such as `useMemo` and `useCallback`. diff --git a/docs/02-app/02-api-reference/05-next-config-js/serverComponentsHmrCache.mdx b/docs/02-app/02-api-reference/05-next-config-js/serverComponentsHmrCache.mdx index 09b2e01612bf6..72a13fef95788 100644 --- a/docs/02-app/02-api-reference/05-next-config-js/serverComponentsHmrCache.mdx +++ b/docs/02-app/02-api-reference/05-next-config-js/serverComponentsHmrCache.mdx @@ -1,7 +1,7 @@ --- title: serverComponentsHmrCache description: Configure whether fetch responses in Server Components are cached across HMR refresh requests. -version: RC +version: experimental --- The experimental `serverComponentsHmrCache` option allows you to cache `fetch` responses in Server Components across Hot Module Replacement (HMR) refreshes in local development. This results in faster responses and reduced costs for billed API calls. From 5558489e840471e8245c4d2527e992502d2fdb25 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 10:29:59 +0100 Subject: [PATCH 56/58] Share next/form between app and pages docs - No examples for pages docs for now, we need to spend more time understanding the patterns there. RC is shipping today. --- .../02-api-reference/01-components/form.mdx | 87 ++++++++++++++++++- .../02-api-reference/01-components/form.mdx | 7 ++ 2 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 docs/03-pages/02-api-reference/01-components/form.mdx diff --git a/docs/02-app/02-api-reference/01-components/form.mdx b/docs/02-app/02-api-reference/01-components/form.mdx index 05d4e19019e2c..fa052e8347964 100644 --- a/docs/02-app/02-api-reference/01-components/form.mdx +++ b/docs/02-app/02-api-reference/01-components/form.mdx @@ -3,13 +3,15 @@ title: <Form> description: Learn how to use the `<Form>` component to handle form submissions and search params updates with client-side navigation. --- -The `<Form>` component extends the HTML `<form>` element to provide [**prefetching**](/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching) of [loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming), **client-side navigation** on submission, and **progressive enhancement**. +The `<Form>` component extends the HTML `<form>` element to provide <AppOnly>[**prefetching**](/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching) of [loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming),</AppOnly> **client-side navigation** on submission, and **progressive enhancement**. It's useful for forms that update URL search params as it reduces the boilerplate code needed to achieve the above. Basic usage: -```tsx filename="/app/page.tsx" switcher +<AppOnly> + +```tsx filename="/app/ui/search.js" switcher import Form from 'next/form' export default function Page() { @@ -39,17 +41,83 @@ export default function Search() { } ``` +</AppOnly> + +<PagesOnly> + +```tsx filename="/ui/search.js" switcher +import Form from 'next/form' + +export default function Page() { + return ( + <Form action="/search"> + {/* On submission, the input value will be appended to + the URL, e.g. /search?query=abc */} + <input name="query" /> + <button type="submit">Submit</button> + </Form> + ) +} +``` + +```jsx filename="/ui/search.js" switcher +import Form from 'next/form' + +export default function Search() { + return ( + <Form action="/search"> + {/* On submission, the input value will be appended to + the URL, e.g. /search?query=abc */} + <input name="query" /> + <button type="submit">Submit</button> + </Form> + ) +} +``` + +</PagesOnly> + ## Reference The behavior of the `<Form>` component depends on whether the `action` prop is passed a `string` or `function`. +<AppOnly> + - When `action` is a **string**, the `<Form>` behaves like a native HTML form that uses a **`GET`** method. The form data is encoded into the URL as search params, and when the form is submitted, it navigates to the specified URL. In addition, Next.js: - [Prefetches](/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching) the path when the form becomes visible, this preloads shared UI (e.g. `layout.js` and `loading.js`), resulting in faster navigation. - Performs a [client-side navigation](/docs/app/building-your-application/routing/linking-and-navigating#5-soft-navigation) instead of a full page reload when the form is submitted. This retains shared UI and client-side state. - When `action` is a **function** (Server Action), `<Form>` behaves like a [React form](https://react.dev/reference/react-dom/components/form), executing the action when the form is submitted. +</AppOnly> + +<PagesOnly> + +- When `action` is a **string**, the `<Form>` behaves like a native HTML form that uses a **`GET`** method. The form data is encoded into the URL as search params, and when the form is submitted, it navigates to the specified URL. In addition, Next.js: + - Performs a [client-side navigation](/docs/app/building-your-application/routing/linking-and-navigating#5-soft-navigation) instead of a full page reload when the form is submitted. This retains shared UI and client-side state. + +</PagesOnly> + ### `action` (string) Props +<PagesOnly> + +When `action` is a string, the `<Form>` component supports the following props: + +| Prop | Example | Type | Required | +| --------- | ------------------ | ------------------------------- | -------- | +| `action` | `action="/search"` | `string` (URL or relative path) | Yes | +| `replace` | `replace={false}` | `boolean` | - | +| `scroll` | `scroll={true}` | `boolean` | - | + +- **`action`**: The URL or path to navigate to when the form is submitted. + - An empty string `""` will navigate to the same route with updated search params. +- **`replace`**: Replaces the current history state instead of pushing a new one to the [browser's history](https://developer.mozilla.org/en-US/docs/Web/API/History_API) stack. Default is `false`. +- **`scroll`**: Controls the scroll behavior during navigation. Defaults to `true`, this means it will scroll to the top of the new route, and maintain the scroll position for backwards and forwards navigation. + +</PagesOnly> + +<AppOnly> + When `action` is a string, the `<Form>` component supports the following props: | Prop | Example | Type | Required | @@ -77,17 +145,26 @@ When `action` is a function, the `<Form>` component supports the following prop: > **Good to know**: When `action` is a function, the `replace` and `scroll` props are ignored. +</AppOnly> + ### Caveats -- **`onSubmit`**: Can be used to handle form submission logic. However, calling `event.preventDefault()` will override `<Form>` behavior such as navigating to the specified URL. +<AppOnly> + - **`formAction`**: Can be used in a `<button>` or `<input type="submit">` fields to override the `action` prop. Next.js will perform a client-side navigation, however, this approach doesn't support prefetching. - When using [`basePath`](/docs/app/api-reference/next-config-js/basePath), you must also include it in the `formAction` path. e.g. `formAction="/base-path/search"`. +- **`key`**: Passing a `key` prop to a string `action` is not supported. If you'd like to trigger a re-render or perform a mutation, consider using a function `action` instead. + +</AppOnly> + +- **`onSubmit`**: Can be used to handle form submission logic. However, calling `event.preventDefault()` will override `<Form>` behavior such as navigating to the specified URL. - **[`method`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method), [`encType`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#enctype), [`target`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#target)**: Are not supported as they override `<Form>` behavior. - Similarly, `formMethod`, `formEncType`, and `formTarget` can be used to override the `method`, `encType`, and `target` props respectively, and using them will fallback to native browser behavior. - If you need to use these props, use the HTML `<form>` element instead. -- **`key`**: Passing a `key` prop to a string `action` is not supported. If you'd like to trigger a re-render or perform a mutation, consider using a function `action` instead. - **`<input type="file">`**: Using this input type when the `action` is a string will match browser behavior by submitting the filename instead of the file object. +<AppOnly> + ## Examples ### Search form that leads to a search result page @@ -319,3 +396,5 @@ export default async function PostPage({ params }) { ``` See the [Server Actions](/docs/app/building-your-application/data-fetching/server-actions-and-mutations) docs for more examples. + +</AppOnly> diff --git a/docs/03-pages/02-api-reference/01-components/form.mdx b/docs/03-pages/02-api-reference/01-components/form.mdx new file mode 100644 index 0000000000000..84ceee76f5368 --- /dev/null +++ b/docs/03-pages/02-api-reference/01-components/form.mdx @@ -0,0 +1,7 @@ +--- +title: <Form> +description: Learn how to use the `<Form>` component to handle form submissions and search params updates with client-side navigation. +source: app/api-reference/components/form +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `<PagesOnly>Content</PagesOnly>` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} From db7885df8b3e9360bde90f6281423d4ee4672599 Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 10:30:06 +0100 Subject: [PATCH 57/58] Fix broken link --- .../02-file-conventions/route-segment-config.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx index e15c739401bed..4c9348493c9ed 100644 --- a/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx +++ b/docs/02-app/02-api-reference/02-file-conventions/route-segment-config.mdx @@ -224,6 +224,6 @@ See the [API reference](/docs/app/api-reference/functions/generate-static-params ## Version History -| Version | | -| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `v15.0.0-RC` | `export const runtime = "experimental-edge"` deprecated. A [codemod](/docs/canary/app/building-your-application/upgrading/codemods#transform-app-router-route-segment-config-runtime-value-from-experimental-edge-to-edge) is available. | +| Version | | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `v15.0.0-RC` | `export const runtime = "experimental-edge"` deprecated. A [codemod](/docs/app/building-your-application/upgrading/codemods#transform-app-router-route-segment-config-runtime-value-from-experimental-edge-to-edge) is available. | From 4000ebb1facba871d43aba880f2cf397d585ba8a Mon Sep 17 00:00:00 2001 From: Delba de Oliveira <delbabrown@gmail.com> Date: Mon, 14 Oct 2024 11:27:04 +0100 Subject: [PATCH 58/58] Document staticGeneration* config options --- .../05-next-config-js/staticGeneration.mdx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/02-app/02-api-reference/05-next-config-js/staticGeneration.mdx diff --git a/docs/02-app/02-api-reference/05-next-config-js/staticGeneration.mdx b/docs/02-app/02-api-reference/05-next-config-js/staticGeneration.mdx new file mode 100644 index 0000000000000..164f07f6bdd85 --- /dev/null +++ b/docs/02-app/02-api-reference/05-next-config-js/staticGeneration.mdx @@ -0,0 +1,41 @@ +--- +title: staticGeneration* +description: Learn how to configure static generation in your Next.js application. +version: experimental +--- + +The `staticGeneration*` options allow you to configure the Static Generation process in your Next.js application. It's helpful for advanced use cases that would benefit from greater control. + +```ts filename="next.config.ts" switcher +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + experimental: { + staticGenerationRetryCount: 1, + staticGenerationMaxConcurrency: 8, + staticGenerationMinPagesPerWorker: 25, + }, +} + +export default nextConfig +``` + +```js filename="next.config.js" switcher +const nextConfig = { + experimental: { + staticGenerationRetryCount: 1 + staticGenerationMaxConcurrency: 8 + staticGenerationMinPagesPerWorker: 25 + }, +} + +export default nextConfig; +``` + +## Config Options + +The following options are available: + +- `staticGenerationRetryCount`: The number of times to retry a failed page generation before failing the build. +- `staticGenerationMaxConcurrency`: The maximum number of pages to be processed per worker. +- `staticGenerationMinPagesPerWorker`: The minimum number of pages to be processed before start a new worker.