From 22fe4cc227c0cf9fa01e69178541e0b2c395202b Mon Sep 17 00:00:00 2001 From: nextjs-bot Date: Fri, 23 Jan 2026 14:00:31 +0000 Subject: [PATCH 1/3] v16.2.0-canary.5 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/eslint-config-next/package.json | 4 ++-- packages/eslint-plugin-internal/package.json | 2 +- packages/eslint-plugin-next/package.json | 2 +- packages/font/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-env/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next-routing/package.json | 2 +- packages/next-rspack/package.json | 2 +- packages/next-swc/package.json | 2 +- packages/next/package.json | 14 +++++++------- packages/react-refresh-utils/package.json | 2 +- packages/third-parties/package.json | 4 ++-- pnpm-lock.yaml | 16 ++++++++-------- 20 files changed, 35 insertions(+), 35 deletions(-) diff --git a/lerna.json b/lerna.json index 777f3a4a13480c..31857d1981d289 100644 --- a/lerna.json +++ b/lerna.json @@ -15,5 +15,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "16.2.0-canary.4" + "version": "16.2.0-canary.5" } \ No newline at end of file diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 58fef370a5a8c1..8e0f2f601a139f 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index 51efb5387c535e..d73acb1f653018 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "ESLint configuration used by Next.js.", "license": "MIT", "repository": { @@ -12,7 +12,7 @@ "dist" ], "dependencies": { - "@next/eslint-plugin-next": "16.2.0-canary.4", + "@next/eslint-plugin-next": "16.2.0-canary.5", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.32.0", diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 6953eb2d3ceac9..8c89d0457f5799 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -1,7 +1,7 @@ { "name": "@next/eslint-plugin-internal", "private": true, - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "ESLint plugin for working on Next.js.", "exports": { ".": "./src/eslint-plugin-internal.js" diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index e2cb308bea1fd8..1465ec1affecf6 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "ESLint plugin for Next.js.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/font/package.json b/packages/font/package.json index e0728cb3ee9b0d..c878c5d04c5935 100644 --- a/packages/font/package.json +++ b/packages/font/package.json @@ -1,7 +1,7 @@ { "name": "@next/font", "private": true, - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "repository": { "url": "vercel/next.js", "directory": "packages/font" diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 72be58551ea664..2f061baf30b076 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "main": "index.js", "types": "index.d.ts", "license": "MIT", diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 293b618488e111..4446e24ebdceb6 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "license": "MIT", "repository": { "type": "git", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 2b4c4c7b76ce9b..f5e45d7cc756c6 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 654f50d3ed1bd0..762ce876dbba23 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 7ccf48da184f3a..274c7139927496 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index fa4b0cfd4c4581..1ce59a1f45280d 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 8c65ed8b82d1fd..d5103b318c31bc 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-routing/package.json b/packages/next-routing/package.json index 70408692c3233a..491f184153016f 100644 --- a/packages/next-routing/package.json +++ b/packages/next-routing/package.json @@ -1,6 +1,6 @@ { "name": "@next/routing", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "keywords": [ "react", "next", diff --git a/packages/next-rspack/package.json b/packages/next-rspack/package.json index f18143497d0efe..1177bf7070c1a3 100644 --- a/packages/next-rspack/package.json +++ b/packages/next-rspack/package.json @@ -1,6 +1,6 @@ { "name": "next-rspack", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "repository": { "url": "vercel/next.js", "directory": "packages/next-rspack" diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index f079f46117de39..cffde7af75bf3f 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "private": true, "files": [ "native/" diff --git a/packages/next/package.json b/packages/next/package.json index 99bd41afd6c590..468687d015eebc 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -97,7 +97,7 @@ ] }, "dependencies": { - "@next/env": "16.2.0-canary.4", + "@next/env": "16.2.0-canary.5", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", @@ -162,11 +162,11 @@ "@modelcontextprotocol/sdk": "1.18.1", "@mswjs/interceptors": "0.23.0", "@napi-rs/triples": "1.2.0", - "@next/font": "16.2.0-canary.4", - "@next/polyfill-module": "16.2.0-canary.4", - "@next/polyfill-nomodule": "16.2.0-canary.4", - "@next/react-refresh-utils": "16.2.0-canary.4", - "@next/swc": "16.2.0-canary.4", + "@next/font": "16.2.0-canary.5", + "@next/polyfill-module": "16.2.0-canary.5", + "@next/polyfill-nomodule": "16.2.0-canary.5", + "@next/react-refresh-utils": "16.2.0-canary.5", + "@next/swc": "16.2.0-canary.5", "@opentelemetry/api": "1.6.0", "@playwright/test": "1.51.1", "@rspack/core": "1.6.7", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index 9cae9b7f3acbb8..15aa456dd5f56c 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", diff --git a/packages/third-parties/package.json b/packages/third-parties/package.json index 516919a9698470..9dccac33abbc77 100644 --- a/packages/third-parties/package.json +++ b/packages/third-parties/package.json @@ -1,6 +1,6 @@ { "name": "@next/third-parties", - "version": "16.2.0-canary.4", + "version": "16.2.0-canary.5", "repository": { "url": "vercel/next.js", "directory": "packages/third-parties" @@ -26,7 +26,7 @@ "third-party-capital": "1.0.20" }, "devDependencies": { - "next": "16.2.0-canary.4", + "next": "16.2.0-canary.5", "outdent": "0.8.0", "prettier": "2.5.1", "typescript": "5.9.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36ca135d62fab8..5583316c6c2488 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1008,7 +1008,7 @@ importers: packages/eslint-config-next: dependencies: '@next/eslint-plugin-next': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../eslint-plugin-next eslint: specifier: '>=9.0.0' @@ -1085,7 +1085,7 @@ importers: packages/next: dependencies: '@next/env': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../next-env '@swc/helpers': specifier: 0.5.15 @@ -1213,19 +1213,19 @@ importers: specifier: 1.2.0 version: 1.2.0 '@next/font': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../font '@next/polyfill-module': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../next-polyfill-module '@next/polyfill-nomodule': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../next-polyfill-nomodule '@next/react-refresh-utils': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../react-refresh-utils '@next/swc': - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../next-swc '@opentelemetry/api': specifier: 1.6.0 @@ -1943,7 +1943,7 @@ importers: version: 1.0.20 devDependencies: next: - specifier: 16.2.0-canary.4 + specifier: 16.2.0-canary.5 version: link:../next outdent: specifier: 0.8.0 From 2b3d4f815bff3ef3a12f3ca5d787032dd6123769 Mon Sep 17 00:00:00 2001 From: Joseph Date: Fri, 23 Jan 2026 17:09:02 +0100 Subject: [PATCH 2/3] docs: Server functions rename (#86827) Closes: https://github.com/vercel/next.js/issues/86781 --- .../01-getting-started/08-updating-data.mdx | 6 ++++-- docs/01-app/02-guides/redirecting.mdx | 18 +++++++++--------- .../03-api-reference/04-functions/after.mdx | 4 ++-- .../03-api-reference/04-functions/cookies.mdx | 18 +++++++++--------- .../04-functions/forbidden.mdx | 2 +- .../04-functions/permanentRedirect.mdx | 2 +- .../03-api-reference/04-functions/redirect.mdx | 2 +- .../04-functions/unauthorized.mdx | 2 +- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/docs/01-app/01-getting-started/08-updating-data.mdx b/docs/01-app/01-getting-started/08-updating-data.mdx index 30548c96c2d1f1..edc38f8d450a8a 100644 --- a/docs/01-app/01-getting-started/08-updating-data.mdx +++ b/docs/01-app/01-getting-started/08-updating-data.mdx @@ -1,6 +1,6 @@ --- title: Updating Data -description: Learn how to mutate data using Server Functions. +description: Learn how to mutate data using Server Functions and Server Actions in Next.js. related: title: API Reference description: Learn more about the features mentioned in this page by reading the API Reference. @@ -18,6 +18,8 @@ A **Server Function** is an asynchronous function that runs on the server. They In an `action` or mutation context, they are also called **Server Actions**. +> **Good to know:** A Server Action is a Server Function used in a specific way (for handling form submissions and mutations). Server Function is the broader term. + By convention, a Server Action is an async function used with [`startTransition`](https://react.dev/reference/react/startTransition). This happens automatically when the function is: - Passed to a `
` using the `action` prop. @@ -427,7 +429,7 @@ Calling `redirect` [throws](/docs/app/api-reference/functions/redirect#behavior) You can `get`, `set`, and `delete` cookies inside a Server Action using the [`cookies`](/docs/app/api-reference/functions/cookies) API. -When you [set or delete](/docs/app/api-reference/functions/cookies#understanding-cookie-behavior-in-server-actions) a cookie in a Server Action, Next.js re-renders the current page and its layouts on the server so the **UI reflects the new cookie value**. +When you [set or delete](/docs/app/api-reference/functions/cookies#understanding-cookie-behavior-in-server-functions) a cookie in a Server Action, Next.js re-renders the current page and its layouts on the server so the **UI reflects the new cookie value**. > **Good to know**: The server update applies to the current React tree, re-rendering, mounting, or unmounting components, as needed. Client state is preserved for re-rendered components, and effects re-run if their dependencies changed. diff --git a/docs/01-app/02-guides/redirecting.mdx b/docs/01-app/02-guides/redirecting.mdx index a2e2b326e5b9bf..2fd83f7ac5451b 100644 --- a/docs/01-app/02-guides/redirecting.mdx +++ b/docs/01-app/02-guides/redirecting.mdx @@ -14,13 +14,13 @@ There are a few ways you can handle redirects in Next.js. This page will go thro -| API | Purpose | Where | Status Code | -| ------------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------- | -------------------------------------- | -| [`redirect`](#redirect-function) | Redirect user after a mutation or event | Server Components, Server Actions, Route Handlers | 307 (Temporary) or 303 (Server Action) | -| [`permanentRedirect`](#permanentredirect-function) | Redirect user after a mutation or event | Server Components, Server Actions, Route Handlers | 308 (Permanent) | -| [`useRouter`](#userouter-hook) | Perform a client-side navigation | Event Handlers in Client Components | N/A | -| [`redirects` in `next.config.js`](#redirects-in-nextconfigjs) | Redirect an incoming request based on a path | `next.config.js` file | 307 (Temporary) or 308 (Permanent) | -| [`NextResponse.redirect`](#nextresponseredirect-in-proxy) | Redirect an incoming request based on a condition | Proxy | Any | +| API | Purpose | Where | Status Code | +| ------------------------------------------------------------- | ------------------------------------------------- | --------------------------------------------------- | -------------------------------------- | +| [`redirect`](#redirect-function) | Redirect user after a mutation or event | Server Components, Server Functions, Route Handlers | 307 (Temporary) or 303 (Server Action) | +| [`permanentRedirect`](#permanentredirect-function) | Redirect user after a mutation or event | Server Components, Server Functions, Route Handlers | 308 (Permanent) | +| [`useRouter`](#userouter-hook) | Perform a client-side navigation | Event Handlers in Client Components | N/A | +| [`redirects` in `next.config.js`](#redirects-in-nextconfigjs) | Redirect an incoming request based on a path | `next.config.js` file | 307 (Temporary) or 308 (Permanent) | +| [`NextResponse.redirect`](#nextresponseredirect-in-proxy) | Redirect an incoming request based on a condition | Proxy | Any | @@ -38,7 +38,7 @@ There are a few ways you can handle redirects in Next.js. This page will go thro ## `redirect` function -The `redirect` function allows you to redirect the user to another URL. You can call `redirect` in [Server Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Actions](/docs/app/getting-started/updating-data). +The `redirect` function allows you to redirect the user to another URL. You can call `redirect` in [Server Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/updating-data). `redirect` is often used after a mutation or event. For example, creating a post: @@ -90,7 +90,7 @@ See the [`redirect` API reference](/docs/app/api-reference/functions/redirect) f ## `permanentRedirect` function -The `permanentRedirect` function allows you to **permanently** redirect the user to another URL. You can call `permanentRedirect` in [Server Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Actions](/docs/app/getting-started/updating-data). +The `permanentRedirect` function allows you to **permanently** redirect the user to another URL. You can call `permanentRedirect` in [Server Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/updating-data). `permanentRedirect` is often used after a mutation or event that changes an entity's canonical URL, such as updating a user's profile URL after they change their username: diff --git a/docs/01-app/03-api-reference/04-functions/after.mdx b/docs/01-app/03-api-reference/04-functions/after.mdx index 6ff8ec63c58c46..3a7d179358d63b 100644 --- a/docs/01-app/03-api-reference/04-functions/after.mdx +++ b/docs/01-app/03-api-reference/04-functions/after.mdx @@ -5,7 +5,7 @@ description: API Reference for the after function. `after` allows you to schedule work to be executed after a response (or prerender) 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/getting-started/server-and-client-components) (including [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata)), [Server Actions](/docs/app/getting-started/updating-data), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Proxy](/docs/app/api-reference/file-conventions/proxy). +It can be used in [Server Components](/docs/app/getting-started/server-and-client-components) (including [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata)), [Server Functions](/docs/app/getting-started/updating-data), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Proxy](/docs/app/api-reference/file-conventions/proxy). The function accepts a callback that will be executed after the response (or prerender) is finished: @@ -59,7 +59,7 @@ export default function Layout({ children }) { ### With request APIs -You can use request APIs such as [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers) inside `after` in [Server Actions](/docs/app/getting-started/updating-data) and [Route Handlers](/docs/app/api-reference/file-conventions/route). This is useful for logging activity after a mutation. For example: +You can use request APIs such as [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers) inside `after` in [Server Functions](/docs/app/getting-started/updating-data) and [Route Handlers](/docs/app/api-reference/file-conventions/route). This is useful for logging activity after a mutation. For example: ```ts filename="app/api/route.ts" highlight={2,7-9} switcher import { after } from 'next/server' diff --git a/docs/01-app/03-api-reference/04-functions/cookies.mdx b/docs/01-app/03-api-reference/04-functions/cookies.mdx index 46066d44274fcf..587bc55237f182 100644 --- a/docs/01-app/03-api-reference/04-functions/cookies.mdx +++ b/docs/01-app/03-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 [Server Components](/docs/app/getting-started/server-and-client-components), and read/write outgoing request cookies in [Server Actions](/docs/app/getting-started/updating-data) or [Route Handlers](/docs/app/api-reference/file-conventions/route). +`cookies` is an **async** function that allows you to read the HTTP incoming request cookies in [Server Components](/docs/app/getting-started/server-and-client-components), and read/write outgoing request cookies in [Server Functions](/docs/app/getting-started/updating-data) or [Route Handlers](/docs/app/api-reference/file-conventions/route). ```tsx filename="app/page.tsx" switcher import { cookies } from 'next/headers' @@ -68,26 +68,26 @@ To learn more about these options, see the [MDN docs](https://developer.mozilla. - In version 14 and earlier, `cookies` was a synchronous function. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future. - `cookies` is a [Dynamic API](/docs/app/guides/caching#dynamic-rendering) 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/guides/caching#dynamic-rendering). - The `.delete` method can only be called: - - In a [Server Action](/docs/app/getting-started/updating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route). + - In a [Server Function](/docs/app/getting-started/updating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route). - If it belongs to the same domain from which `.set` is called. For wildcard domains, the specific subdomain must be an exact match. 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/getting-started/updating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route). +- HTTP does not allow setting cookies after streaming starts, so you must use `.set` in a [Server Function](/docs/app/getting-started/updating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route). ## Understanding Cookie Behavior in Server Components When working with cookies in Server Components, it's important to understand that cookies are fundamentally a client-side storage mechanism: - **Reading cookies** works in Server Components because you're accessing the cookie data that the client's browser sends to the server in the HTTP request headers. -- **Setting cookies** cannot be done directly in a Server Component, even when using a Route Handler or Server Action. This is because cookies are actually stored by the browser, not the server. +- **Setting cookies** is not supported during Server Component rendering. To modify cookies, invoke a Server Function from the client or use a Route Handler. -The server can only send instructions (via `Set-Cookie` headers) to tell the browser to store cookies - the actual storage happens on the client side. This is why cookie operations that modify state (`.set`, `.delete`) must be performed in a Route Handler or Server Action where the response headers can be properly set. +The server can only send instructions (via `Set-Cookie` headers) to tell the browser to store cookies - the actual storage happens on the client side. This is why cookie operations that modify state (`.set`, `.delete`) must be performed in a Server Function or Route Handler where the response headers can be properly set. -## Understanding Cookie Behavior in Server Actions +## Understanding Cookie Behavior in Server Functions -After you set or delete a cookie in a Server Action, Next.js re-renders the current page and its layouts on the server so the UI reflects the new cookie value. See the [Caching guide](/docs/app/guides/caching#cookies). +After you set or delete a cookie in a Server Function, Next.js can return both the updated UI and new data in a single server roundtrip when the function is used as a [Server Action](/docs/app/getting-started/updating-data#what-are-server-functions) (e.g., passed to a form's `action` prop). See the [Caching guide](/docs/app/guides/caching#cookies). The UI is not unmounted, but effects that depend on data coming from the server will re-run. -To refresh cached data too, call [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) inside the action. +To refresh cached data too, call [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) inside the function. ## Examples @@ -149,7 +149,7 @@ export default async function Page() { ### Setting a cookie -You can use the `(await cookies()).set(name, value, options)` method in a [Server Action](/docs/app/getting-started/updating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route) to set a cookie. The [`options` object](#options) is optional. +You can use the `(await cookies()).set(name, value, options)` method in a [Server Function](/docs/app/getting-started/updating-data) or [Route Handler](/docs/app/api-reference/file-conventions/route) to set a cookie. The [`options` object](#options) is optional. ```tsx filename="app/actions.ts" switcher 'use server' diff --git a/docs/01-app/03-api-reference/04-functions/forbidden.mdx b/docs/01-app/03-api-reference/04-functions/forbidden.mdx index 343f4cc5b928a1..1e8f2d6b2e1e4f 100644 --- a/docs/01-app/03-api-reference/04-functions/forbidden.mdx +++ b/docs/01-app/03-api-reference/04-functions/forbidden.mdx @@ -31,7 +31,7 @@ module.exports = { } ``` -`forbidden` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Actions](/docs/app/getting-started/updating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route). +`forbidden` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Functions](/docs/app/getting-started/updating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route). ```tsx filename="app/auth/page.tsx" switcher import { verifySession } from '@/app/lib/dal' diff --git a/docs/01-app/03-api-reference/04-functions/permanentRedirect.mdx b/docs/01-app/03-api-reference/04-functions/permanentRedirect.mdx index fb29d2434b6c99..eeec41c101da22 100644 --- a/docs/01-app/03-api-reference/04-functions/permanentRedirect.mdx +++ b/docs/01-app/03-api-reference/04-functions/permanentRedirect.mdx @@ -6,7 +6,7 @@ related: - app/api-reference/functions/redirect --- -The `permanentRedirect` function allows you to redirect the user to another URL. `permanentRedirect` can be used in Server Components, Client Components, [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Actions](/docs/app/getting-started/updating-data). +The `permanentRedirect` function allows you to redirect the user to another URL. `permanentRedirect` can be used in Server Components, Client Components, [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/updating-data). When used in a streaming context, this will insert a meta tag to emit the redirect on the client side. When used in a server action, it will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 308 (Permanent) HTTP redirect response to the caller. diff --git a/docs/01-app/03-api-reference/04-functions/redirect.mdx b/docs/01-app/03-api-reference/04-functions/redirect.mdx index e161f5b1cc5881..dbd0264111e937 100644 --- a/docs/01-app/03-api-reference/04-functions/redirect.mdx +++ b/docs/01-app/03-api-reference/04-functions/redirect.mdx @@ -6,7 +6,7 @@ related: - app/api-reference/functions/permanentRedirect --- -The `redirect` function allows you to redirect the user to another URL. `redirect` can be used while rendering in [Server and Client Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Actions](/docs/app/getting-started/updating-data). +The `redirect` function allows you to redirect the user to another URL. `redirect` can be used while rendering in [Server and Client Components](/docs/app/getting-started/server-and-client-components), [Route Handlers](/docs/app/api-reference/file-conventions/route), and [Server Functions](/docs/app/getting-started/updating-data). When used in a [streaming context](/docs/app/getting-started/linking-and-navigating#streaming), this will insert a meta tag to emit the redirect on the client side. When used in a server action, it will serve a 303 HTTP redirect response to the caller. Otherwise, it will serve a 307 HTTP redirect response to the caller. diff --git a/docs/01-app/03-api-reference/04-functions/unauthorized.mdx b/docs/01-app/03-api-reference/04-functions/unauthorized.mdx index 6018acb9ac61b5..93cbb006b418a7 100644 --- a/docs/01-app/03-api-reference/04-functions/unauthorized.mdx +++ b/docs/01-app/03-api-reference/04-functions/unauthorized.mdx @@ -31,7 +31,7 @@ module.exports = { } ``` -`unauthorized` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Actions](/docs/app/getting-started/updating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route). +`unauthorized` can be invoked in [Server Components](/docs/app/getting-started/server-and-client-components), [Server Functions](/docs/app/getting-started/updating-data), and [Route Handlers](/docs/app/api-reference/file-conventions/route). ```tsx filename="app/dashboard/page.tsx" switcher import { verifySession } from '@/app/lib/dal' From 20be9bb47a69e850ec474e0a60ea9e6d8e998d8a Mon Sep 17 00:00:00 2001 From: Luke Sandberg Date: Fri, 23 Jan 2026 10:15:11 -0800 Subject: [PATCH 3/3] [turbopack] add task type infromation to the print_cache_item_size feature (#88925) Enhance our debugging output to include information about TaskType values example output: ``` Task type cache stats: 337.82MiB (364.22MiB) 49.98MiB (53.43MiB) ::resolve_asset x 417238 avg 125B (134B) 29.38MiB (31.46MiB) resolve x 297288 avg 103B (110B) 17.97MiB (22.58MiB) AssetIdent::new_inner x 135310 avg 139B (174B) 15.16MiB (21.86MiB) ::after_resolve x 90748 avg 175B (252B) 12.36MiB (13.32MiB) resolve_internal x 138970 avg 93B (100B) 8.44MiB (7.86MiB) ResolveOrigin::resolve_options x 301586 avg 29B (27B) 8.43MiB (9.47MiB) AfterResolvePluginCondition::matches x 87740 avg 100B (113B) 6.70MiB (6.23MiB) ::resolve_reference x 472105 avg 14B (13B) 6.48MiB (7.81MiB) read_matches x 81046 avg 83B (101B) 6.40MiB (5.95MiB) ::chunking_type x 451598 avg 14B (13B) 6.39MiB (5.94MiB) ::binding_usage x 450844 avg 14B (13B) 6.03MiB (5.68MiB) ::process_resolve_result x 184982 avg 34B (32B) 5.99MiB (5.57MiB) EsmAssetReference::get_referenced_asset x 422883 avg 14B (13B) ``` also add summary stats to the 'task cache stats' ``` Task cache stats: 3.23GiB (6.49GiB) 302.72MiB (769.45MiB) ::generate_source_map = 313.04KiB (308.92KiB) meta 8524 x 37B (37B), 302.42MiB (769.15MiB) data 8524 x 36.33KiB (92.40KiB) 212.75MiB (592.88MiB) module_factory_with_code_generation_issue = 1.59MiB (1.51MiB) meta 45906 x 36B (34B), 211.16MiB (591.37MiB) data 45906 x 4.71KiB (13.19KiB) 207.89MiB (526.37MiB) EcmascriptBuildNodeChunkContent::code = 1.08MiB (1.12MiB) meta 5163 x 219B (228B), 206.81MiB (525.24MiB) data 5163 x 41.02KiB (104.17KiB) 205.21MiB (577.16MiB) EcmascriptChunkItemContent::new = 1.56MiB (1.63MiB) meta 24577 x 66B (69B), 203.65MiB (575.53MiB) data 24577 x 8.48KiB (23.98KiB) 179.00MiB (441.68MiB) EcmascriptBrowserChunkContent::code = 905.40KiB (967.46KiB) meta 2485 x 373B (398B), 178.11MiB (440.74MiB) data 2485 x 73.40KiB (181.62KiB) ``` Using this information i can see that the largest task type is ~365b so we can safely decrease the size of our scratch buffer --- .../turbo-tasks-backend/src/backend/mod.rs | 20 ++++- .../src/kv_backing_storage.rs | 86 ++++++++++++++++++- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs index 819bc98664ba54..c861afb69c7f42 100644 --- a/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs +++ b/turbopack/crates/turbo-tasks-backend/src/backend/mod.rs @@ -1177,14 +1177,28 @@ impl TurboTasksBackendInner { .into_iter() .collect::>(); if !task_cache_stats.is_empty() { + use turbo_tasks::util::FormatBytes; task_cache_stats.sort_unstable_by(|(key_a, stats_a), (key_b, stats_b)| { (stats_b.data_compressed + stats_b.meta_compressed, key_b) .cmp(&(stats_a.data_compressed + stats_a.meta_compressed, key_a)) }); - println!("Task cache stats:"); - for (task_desc, stats) in task_cache_stats { - use turbo_tasks::util::FormatBytes; + println!( + "Task cache stats: {} ({})", + FormatBytes( + task_cache_stats + .iter() + .map(|(_, s)| s.data_compressed + s.meta_compressed) + .sum::() + ), + FormatBytes( + task_cache_stats + .iter() + .map(|(_, s)| s.data + s.meta) + .sum::() + ) + ); + for (task_desc, stats) in task_cache_stats { println!( " {} ({}) {task_desc} = {} ({}) meta {} x {} ({}), {} ({}) data {} x \ {} ({})", diff --git a/turbopack/crates/turbo-tasks-backend/src/kv_backing_storage.rs b/turbopack/crates/turbo-tasks-backend/src/kv_backing_storage.rs index 02de0c5ff9ca51..198079c9abfb23 100644 --- a/turbopack/crates/turbo-tasks-backend/src/kv_backing_storage.rs +++ b/turbopack/crates/turbo-tasks-backend/src/kv_backing_storage.rs @@ -279,8 +279,13 @@ impl BackingStorageSealed let mut batch = self.inner.database.write_batch()?; // these buffers should be large, because they're temporary and re-used. - const INITIAL_ENCODE_BUFFER_CAPACITY: usize = 1024; - + // From measuring a large application the largest TaskType was ~365b, so this should be big + // enough to trigger no resizes in the loop. + const INITIAL_ENCODE_BUFFER_CAPACITY: usize = 512; + #[cfg(feature = "print_cache_item_size")] + let all_stats: std::sync::Mutex< + std::collections::HashMap<&'static str, TaskTypeCacheStats>, + > = std::sync::Mutex::new(std::collections::HashMap::new()); // Start organizing the updates in parallel match &mut batch { &mut WriteBatch::Concurrent(ref batch, _) => { @@ -337,6 +342,13 @@ impl BackingStorageSealed "Unable to write task cache {task_type:?} => {task_id}" ) })?; + #[cfg(feature = "print_cache_item_size")] + all_stats + .lock() + .unwrap() + .entry(task_type.get_name()) + .or_default() + .add(&task_type_bytes); max_task_id = max_task_id.max(task_id); } @@ -411,6 +423,13 @@ impl BackingStorageSealed .with_context(|| { format!("Unable to write task cache {task_type:?} => {task_id}") })?; + #[cfg(feature = "print_cache_item_size")] + all_stats + .lock() + .unwrap() + .entry(task_type.get_name()) + .or_default() + .add(&task_type_bytes); next_task_id = next_task_id.max(task_id + 1); } } @@ -422,6 +441,8 @@ impl BackingStorageSealed )?; } } + #[cfg(feature = "print_cache_item_size")] + print_task_type_cache_stats(all_stats.into_inner().unwrap()); { let _span = tracing::trace_span!("commit").entered(); @@ -668,6 +689,67 @@ type SerializedTasks = Vec< )>, >; +#[cfg(feature = "print_cache_item_size")] +#[derive(Default)] +struct TaskTypeCacheStats { + key_size: usize, + key_size_compressed: usize, + count: usize, +} + +#[cfg(feature = "print_cache_item_size")] +impl TaskTypeCacheStats { + fn compressed_size(data: &[u8]) -> Result { + Ok(lzzzz::lz4::Compressor::new()?.next_to_vec( + data, + &mut Vec::new(), + lzzzz::lz4::ACC_LEVEL_DEFAULT, + )?) + } + fn add(&mut self, key_bytes: &[u8]) { + self.key_size += key_bytes.len(); + self.key_size_compressed += Self::compressed_size(key_bytes).unwrap_or(0); + self.count += 1; + } +} + +#[cfg(feature = "print_cache_item_size")] +fn print_task_type_cache_stats(stats: std::collections::HashMap<&'static str, TaskTypeCacheStats>) { + use turbo_tasks::util::FormatBytes; + + let mut stats: Vec<_> = stats.into_iter().collect(); + if stats.is_empty() { + return; + } + stats.sort_unstable_by(|(key_a, stats_a), (key_b, stats_b)| { + (stats_b.key_size_compressed, *key_b).cmp(&(stats_a.key_size_compressed, *key_a)) + }); + println!( + "Task type cache stats: {} ({})", + FormatBytes( + stats + .iter() + .map(|(_, s)| s.key_size_compressed) + .sum::() + ), + FormatBytes(stats.iter().map(|(_, s)| s.key_size).sum::()) + ); + for (fn_name, stats) in stats { + println!( + " {} ({}) {fn_name} x {} avg {} ({})", + FormatBytes(stats.key_size_compressed), + FormatBytes(stats.key_size), + stats.count, + FormatBytes( + stats + .key_size_compressed + .checked_div(stats.count) + .unwrap_or(0) + ), + FormatBytes(stats.key_size.checked_div(stats.count).unwrap_or(0)), + ); + } +} fn process_task_data<'a, B: ConcurrentWriteBatch<'a> + Send + Sync, I>( tasks: Vec, batch: Option<&B>,