next 16.2.8 → 16.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/.build-commit +1 -1
  2. package/dist/bin/next +2 -2
  3. package/dist/bin/next.map +0 -0
  4. package/dist/build/index.js +3 -3
  5. package/dist/build/swc/index.js +1 -1
  6. package/dist/build/turbopack-analyze/index.js +1 -1
  7. package/dist/build/turbopack-build/impl.js +1 -1
  8. package/dist/build/webpack-config.js +3 -3
  9. package/dist/bundle-analyzer/404.html +2 -2
  10. package/dist/bundle-analyzer/__next.__PAGE__.txt +1 -1
  11. package/dist/bundle-analyzer/__next._full.txt +1 -1
  12. package/dist/bundle-analyzer/__next._head.txt +1 -1
  13. package/dist/bundle-analyzer/__next._index.txt +1 -1
  14. package/dist/bundle-analyzer/__next._tree.txt +1 -1
  15. package/dist/bundle-analyzer/_not-found/__next._full.txt +1 -1
  16. package/dist/bundle-analyzer/_not-found/__next._head.txt +1 -1
  17. package/dist/bundle-analyzer/_not-found/__next._index.txt +1 -1
  18. package/dist/bundle-analyzer/_not-found/__next._not-found.__PAGE__.txt +1 -1
  19. package/dist/bundle-analyzer/_not-found/__next._not-found.txt +1 -1
  20. package/dist/bundle-analyzer/_not-found/__next._tree.txt +1 -1
  21. package/dist/bundle-analyzer/_not-found.html +2 -2
  22. package/dist/bundle-analyzer/_not-found.txt +1 -1
  23. package/dist/bundle-analyzer/index.html +2 -2
  24. package/dist/bundle-analyzer/index.txt +1 -1
  25. package/dist/client/app-bootstrap.js +1 -1
  26. package/dist/client/index.js +1 -1
  27. package/dist/compiled/next-server/pages-api-turbo.runtime.dev.js +1 -1
  28. package/dist/compiled/next-server/pages-api-turbo.runtime.dev.js.map +1 -1
  29. package/dist/compiled/next-server/pages-api.runtime.dev.js +1 -1
  30. package/dist/compiled/next-server/pages-api.runtime.dev.js.map +1 -1
  31. package/dist/compiled/next-server/pages-api.runtime.prod.js +1 -1
  32. package/dist/compiled/next-server/pages-api.runtime.prod.js.map +1 -1
  33. package/dist/compiled/next-server/pages-turbo.runtime.dev.js +1 -1
  34. package/dist/compiled/next-server/pages-turbo.runtime.dev.js.map +1 -1
  35. package/dist/compiled/next-server/pages.runtime.dev.js +1 -1
  36. package/dist/compiled/next-server/pages.runtime.dev.js.map +1 -1
  37. package/dist/compiled/next-server/pages.runtime.prod.js +1 -1
  38. package/dist/compiled/next-server/pages.runtime.prod.js.map +1 -1
  39. package/dist/compiled/next-server/server.runtime.prod.js +1 -1
  40. package/dist/compiled/next-server/server.runtime.prod.js.map +1 -1
  41. package/dist/docs/01-app/01-getting-started/02-project-structure.md +1 -1
  42. package/dist/docs/01-app/01-getting-started/03-layouts-and-pages.md +6 -4
  43. package/dist/docs/01-app/01-getting-started/04-linking-and-navigating.md +1 -1
  44. package/dist/docs/01-app/01-getting-started/05-server-and-client-components.md +1 -1
  45. package/dist/docs/01-app/01-getting-started/07-mutating-data.md +4 -3
  46. package/dist/docs/01-app/01-getting-started/09-revalidating.md +9 -8
  47. package/dist/docs/01-app/01-getting-started/13-fonts.md +1 -1
  48. package/dist/docs/01-app/01-getting-started/14-metadata-and-og-images.md +23 -9
  49. package/dist/docs/01-app/01-getting-started/17-deploying.md +1 -1
  50. package/dist/docs/01-app/02-guides/authentication.md +5 -5
  51. package/dist/docs/01-app/02-guides/backend-for-frontend.md +20 -8
  52. package/dist/docs/01-app/02-guides/cdn-caching.md +1 -1
  53. package/dist/docs/01-app/02-guides/custom-server.md +1 -1
  54. package/dist/docs/01-app/02-guides/data-security.md +19 -12
  55. package/dist/docs/01-app/02-guides/draft-mode.md +168 -105
  56. package/dist/docs/01-app/02-guides/forms.md +1 -1
  57. package/dist/docs/01-app/02-guides/how-revalidation-works.md +2 -2
  58. package/dist/docs/01-app/02-guides/mdx.md +1 -0
  59. package/dist/docs/01-app/02-guides/migrating-to-cache-components.md +533 -3
  60. package/dist/docs/01-app/02-guides/multi-zones.md +1 -1
  61. package/dist/docs/01-app/02-guides/package-bundling.md +1 -1
  62. package/dist/docs/01-app/02-guides/prefetching.md +5 -5
  63. package/dist/docs/01-app/02-guides/preventing-flash-before-hydration.md +3 -3
  64. package/dist/docs/01-app/02-guides/public-static-pages.md +1 -1
  65. package/dist/docs/01-app/02-guides/scripts.md +1 -1
  66. package/dist/docs/01-app/02-guides/server-actions.md +180 -0
  67. package/dist/docs/01-app/02-guides/streaming.md +0 -1
  68. package/dist/docs/01-app/02-guides/third-party-libraries.md +1 -0
  69. package/dist/docs/01-app/02-guides/upgrading/version-15.md +1 -1
  70. package/dist/docs/01-app/03-api-reference/01-directives/use-server.md +2 -0
  71. package/dist/docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.md +4 -2
  72. package/dist/docs/01-app/03-api-reference/03-file-conventions/instrumentation.md +18 -4
  73. package/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/rewrites.md +8 -6
  74. package/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/serverActions.md +2 -0
  75. package/dist/docs/01-app/03-api-reference/05-config/01-next-config-js/transpilePackages.md +15 -3
  76. package/dist/docs/01-app/03-api-reference/05-config/02-typescript.md +1 -1
  77. package/dist/docs/01-app/04-glossary.md +1 -1
  78. package/dist/docs/02-pages/02-guides/upgrading/version-12.md +1 -1
  79. package/dist/docs/04-community/01-contribution-guide.md +2 -2
  80. package/dist/docs/index.md +0 -2
  81. package/dist/esm/build/index.js +3 -3
  82. package/dist/esm/build/swc/index.js +1 -1
  83. package/dist/esm/build/turbopack-analyze/index.js +1 -1
  84. package/dist/esm/build/turbopack-build/impl.js +1 -1
  85. package/dist/esm/build/webpack-config.js +3 -3
  86. package/dist/esm/client/app-bootstrap.js +1 -1
  87. package/dist/esm/client/index.js +1 -1
  88. package/dist/esm/lib/patch-incorrect-lockfile.js +3 -3
  89. package/dist/esm/server/config.js +1 -1
  90. package/dist/esm/server/dev/hot-reloader-turbopack.js +2 -2
  91. package/dist/esm/server/dev/hot-reloader-webpack.js +1 -1
  92. package/dist/esm/server/lib/app-info-log.js +1 -1
  93. package/dist/esm/server/lib/start-server.js +1 -1
  94. package/dist/esm/shared/lib/errors/canary-only-config-error.js +1 -1
  95. package/dist/lib/patch-incorrect-lockfile.js +3 -3
  96. package/dist/server/config.js +1 -1
  97. package/dist/server/dev/hot-reloader-turbopack.js +2 -2
  98. package/dist/server/dev/hot-reloader-webpack.js +1 -1
  99. package/dist/server/lib/app-info-log.js +1 -1
  100. package/dist/server/lib/start-server.js +1 -1
  101. package/dist/shared/lib/errors/canary-only-config-error.js +1 -1
  102. package/dist/telemetry/anonymous-meta.js +1 -1
  103. package/dist/telemetry/events/session-stopped.js +2 -2
  104. package/dist/telemetry/events/swc-load-failure.js +1 -1
  105. package/dist/telemetry/events/version.js +2 -2
  106. package/package.json +10 -252
  107. /package/dist/bundle-analyzer/_next/static/{ShlsHWUU3q2GhICNPxAIf → -wnBUGOTjuvnrsp1fJxyv}/_buildManifest.js +0 -0
  108. /package/dist/bundle-analyzer/_next/static/{ShlsHWUU3q2GhICNPxAIf → -wnBUGOTjuvnrsp1fJxyv}/_clientMiddlewareManifest.json +0 -0
  109. /package/dist/bundle-analyzer/_next/static/{ShlsHWUU3q2GhICNPxAIf → -wnBUGOTjuvnrsp1fJxyv}/_ssgManifest.js +0 -0
@@ -122,7 +122,7 @@ Use `@slot` for named slots rendered by a parent layout. Use intercept patterns
122
122
  | [`favicon`](/docs/app/api-reference/file-conventions/metadata/app-icons#favicon) | `.ico` | Favicon file |
123
123
  | [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#icon) | `.ico` `.jpg` `.jpeg` `.png` `.svg` | App Icon file |
124
124
  | [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated App Icon |
125
- | [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#apple-icon) | `.jpg` `.jpeg`, `.png` | Apple App Icon file |
125
+ | [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#apple-icon) | `.jpg` `.jpeg` `.png` | Apple App Icon file |
126
126
  | [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Apple App Icon |
127
127
 
128
128
  #### Open Graph and Twitter images
@@ -287,10 +287,11 @@ You can use the [`<Link>` component](/docs/app/api-reference/components/link) to
287
287
 
288
288
  For example, to generate a list of blog posts, import `<Link>` from `next/link` and pass a `href` prop to the component:
289
289
 
290
- ```tsx filename="app/ui/post.tsx" highlight={1,10} switcher
290
+ ```tsx filename="app/ui/post.tsx" highlight={1,2,11} switcher
291
291
  import Link from 'next/link'
292
+ import { getPosts } from '@/lib/posts'
292
293
 
293
- export default async function Post({ post }) {
294
+ export default async function Posts() {
294
295
  const posts = await getPosts()
295
296
 
296
297
  return (
@@ -305,10 +306,11 @@ export default async function Post({ post }) {
305
306
  }
306
307
  ```
307
308
 
308
- ```jsx filename="app/ui/post.js" highlight={1,10} switcher
309
+ ```jsx filename="app/ui/post.js" highlight={1,2,11} switcher
309
310
  import Link from 'next/link'
311
+ import { getPosts } from '@/lib/posts'
310
312
 
311
- export default async function Post({ post }) {
313
+ export default async function Posts() {
312
314
  const posts = await getPosts()
313
315
 
314
316
  return (
@@ -65,7 +65,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
65
65
  ```jsx filename="app/layout.js" switcher
66
66
  import Link from 'next/link'
67
67
 
68
- export default function Layout() {
68
+ export default function Layout({ children }) {
69
69
  return (
70
70
  <html>
71
71
  <body>
@@ -14,7 +14,7 @@ This page explains how Server and Client Components work in Next.js and when to
14
14
 
15
15
  ## When to use Server and Client Components?
16
16
 
17
- The client and server environments have different capabilities. Server and Client components allow you to run logic in each environment depending on your use case.
17
+ The client and server environments have different capabilities. Server and Client Components allow you to run logic in each environment depending on your use case.
18
18
 
19
19
  Use **Client Components** when you need:
20
20
 
@@ -2,15 +2,16 @@
2
2
  title: Mutating Data
3
3
  description: Learn how to mutate data using Server Functions and Server Actions in Next.js.
4
4
  related:
5
- title: API Reference
6
- description: Learn more about the features mentioned in this page by reading the API Reference.
5
+ title: Next steps
6
+ description: Learn more about Server Actions and the APIs mentioned in this page.
7
7
  links:
8
+ - app/guides/server-actions
8
9
  - app/api-reference/functions/revalidatePath
9
10
  - app/api-reference/functions/revalidateTag
10
11
  - app/api-reference/functions/redirect
11
12
  ---
12
13
 
13
- You can mutate data in Next.js using [React Server Functions](https://react.dev/reference/rsc/server-functions). This page will go through how you can [create](#creating-server-functions) and [invoke](#invoking-server-functions) Server Functions.
14
+ You can mutate data in Next.js using [React Server Functions](https://react.dev/reference/rsc/server-functions). This page will go through how you can [create](#creating-server-functions) and [invoke](#invoking-server-functions) Server Functions. For Next.js-specific behaviors (single-roundtrip response, sequential dispatch, security, deployment), see [Server Actions and Mutations](/docs/app/guides/server-actions).
14
15
 
15
16
  ## What are Server Functions?
16
17
 
@@ -35,14 +35,15 @@ export async function getProducts() {
35
35
 
36
36
  `cacheLife` accepts a profile name or a custom configuration object:
37
37
 
38
- | Profile | `stale` | `revalidate` | `expire` |
39
- | --------- | ------- | ------------ | ----------- |
40
- | `seconds` | 0 | 1s | 60s |
41
- | `minutes` | 5m | 1m | 1h |
42
- | `hours` | 5m | 1h | 1d |
43
- | `days` | 5m | 1d | 1w |
44
- | `weeks` | 5m | 1w | 30d |
45
- | `max` | 5m | 30d | ~indefinite |
38
+ | Profile | `stale` | `revalidate` | `expire` |
39
+ | --------- | ------- | ------------ | -------- |
40
+ | `default` | 5m | 15m | never |
41
+ | `seconds` | 30s | 1s | 60s |
42
+ | `minutes` | 5m | 1m | 1h |
43
+ | `hours` | 5m | 1h | 1d |
44
+ | `days` | 5m | 1d | 1w |
45
+ | `weeks` | 5m | 1w | 30d |
46
+ | `max` | 5m | 30d | 1y |
46
47
 
47
48
  For fine-grained control, pass an object:
48
49
 
@@ -93,7 +93,7 @@ export default function MyApp({ Component, pageProps }) {
93
93
 
94
94
  ## Google fonts
95
95
 
96
- You can automatically self-host any Google Font. Fonts are included stored as static assets and served from the same domain as your deployment, meaning no requests are sent to Google by the browser when the user visits your site.
96
+ You can automatically self-host any Google Font. Fonts are included as static assets and served from the same domain as your deployment, meaning no requests are sent to Google by the browser when the user visits your site.
97
97
 
98
98
  To start using a Google Font, import your chosen font from `next/font/google`:
99
99
 
@@ -162,17 +162,23 @@ import { getPost } from '@/app/lib/data'
162
162
  export async function generateMetadata({
163
163
  params,
164
164
  }: {
165
- params: { slug: string }
165
+ params: Promise<{ slug: string }>
166
166
  }) {
167
- const post = await getPost(params.slug)
167
+ const { slug } = await params
168
+ const post = await getPost(slug)
168
169
  return {
169
170
  title: post.title,
170
171
  description: post.description,
171
172
  }
172
173
  }
173
174
 
174
- export default async function Page({ params }: { params: { slug: string } }) {
175
- const post = await getPost(params.slug)
175
+ export default async function Page({
176
+ params,
177
+ }: {
178
+ params: Promise<{ slug: string }>
179
+ }) {
180
+ const { slug } = await params
181
+ const post = await getPost(slug)
176
182
  return <div>{post.title}</div>
177
183
  }
178
184
  ```
@@ -181,7 +187,8 @@ export default async function Page({ params }: { params: { slug: string } }) {
181
187
  import { getPost } from '@/app/lib/data'
182
188
 
183
189
  export async function generateMetadata({ params }) {
184
- const post = await getPost(params.slug)
190
+ const { slug } = await params
191
+ const post = await getPost(slug)
185
192
  return {
186
193
  title: post.title,
187
194
  description: post.description,
@@ -189,7 +196,8 @@ export async function generateMetadata({ params }) {
189
196
  }
190
197
 
191
198
  export default async function Page({ params }) {
192
- const post = await getPost(params.slug)
199
+ const { slug } = await params
200
+ const post = await getPost(slug)
193
201
  return <div>{post.title}</div>
194
202
  }
195
203
  ```
@@ -264,8 +272,13 @@ export const size = {
264
272
  export const contentType = 'image/png'
265
273
 
266
274
  // Image generation
267
- export default async function Image({ params }: { params: { slug: string } }) {
268
- const post = await getPost(params.slug)
275
+ export default async function Image({
276
+ params,
277
+ }: {
278
+ params: Promise<{ slug: string }>
279
+ }) {
280
+ const { slug } = await params
281
+ const post = await getPost(slug)
269
282
 
270
283
  return new ImageResponse(
271
284
  (
@@ -302,7 +315,8 @@ export const contentType = 'image/png'
302
315
 
303
316
  // Image generation
304
317
  export default async function Image({ params }) {
305
- const post = await getPost(params.slug)
318
+ const { slug } = await params
319
+ const post = await getPost(slug)
306
320
 
307
321
  return new ImageResponse(
308
322
  (
@@ -39,7 +39,7 @@ Node.js deployments support all Next.js features. Learn how to [configure them](
39
39
 
40
40
  ## Docker
41
41
 
42
- Next.js can be deployed to any provider that supports [Docker](https://www.docker.com/) containers. This includes container orchestrators like Kubernetes or a cloud provider that runs Docker. For containerization best practices, see the [Docker guide for React.js](https://docs.docker.com/guides/reactjs/).
42
+ Next.js can be deployed to any provider that supports [Docker](https://www.docker.com/) containers. This includes container orchestrators like Kubernetes or a cloud provider that runs Docker. For best practices on containerizing your app, refer to Docker's official [Next.js](https://docs.docker.com/guides/nextjs) and [React.js](https://docs.docker.com/guides/reactjs) guides.
43
43
 
44
44
  Docker deployments support all Next.js features. Learn how to [configure them](/docs/app/guides/self-hosting) for your infrastructure.
45
45
 
@@ -749,16 +749,16 @@ import { cookies } from 'next/headers'
749
749
  import { decrypt } from '@/app/lib/session'
750
750
 
751
751
  export async function updateSession() {
752
- const session = (await cookies()).get('session')?.value
752
+ const cookieStore = await cookies()
753
+ const session = cookieStore.get('session')?.value
753
754
  const payload = await decrypt(session)
754
755
 
755
756
  if (!session || !payload) {
756
757
  return null
757
758
  }
758
759
 
759
- const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)(
760
- await cookies()
761
- ).set('session', session, {
760
+ const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
761
+ cookieStore.set('session', session, {
762
762
  httpOnly: true,
763
763
  secure: true,
764
764
  expires: expires,
@@ -1120,7 +1120,7 @@ While Proxy can be useful for initial checks, it should not be your only line of
1120
1120
 
1121
1121
  > **Tips**:
1122
1122
  >
1123
- > - In Proxy, you can also read cookies using `req.cookies.get('session').value`.
1123
+ > - In Proxy, you can also read cookies using `req.cookies.get('session')?.value`.
1124
1124
  > - Proxy uses the Node.js runtime, check if your Auth library and session management library are compatible. You may need to use [Middleware](https://github.com/vercel/next.js/blob/v15.5.6/docs/01-app/03-api-reference/03-file-conventions/middleware.mdx) if your Auth library only supports [Edge Runtime](/docs/app/api-reference/edge)
1125
1125
  > - You can use the `matcher` property in the Proxy to specify which routes Proxy should run on. Although, for auth, it's recommended Proxy runs on all routes.
1126
1126
 
@@ -351,9 +351,9 @@ export async function POST(request: Request) {
351
351
  try {
352
352
  const clonedRequest = request.clone()
353
353
 
354
- await request.body()
355
- await clonedRequest.body()
356
- await request.body() // Throws error
354
+ await request.text()
355
+ await clonedRequest.text()
356
+ await request.text() // Throws error
357
357
 
358
358
  return new Response(null, { status: 204 })
359
359
  } catch {
@@ -367,9 +367,9 @@ export async function POST(request) {
367
367
  try {
368
368
  const clonedRequest = request.clone()
369
369
 
370
- await request.body()
371
- await clonedRequest.body()
372
- await request.body() // Throws error
370
+ await request.text()
371
+ await clonedRequest.text()
372
+ await request.text() // Throws error
373
373
 
374
374
  return new Response(null, { status: 204 })
375
375
  } catch {
@@ -612,7 +612,13 @@ export async function GET(request: NextRequest) {
612
612
  const token = request.nextUrl.searchParams.get('session_token')
613
613
  const redirectUrl = request.nextUrl.searchParams.get('redirect_url')
614
614
 
615
- const response = NextResponse.redirect(new URL(redirectUrl, request.url))
615
+ const destination = new URL(redirectUrl ?? '/', request.url)
616
+ // Prevent open redirects: only allow same-origin destinations
617
+ if (destination.origin !== request.nextUrl.origin) {
618
+ return new Response('Invalid redirect', { status: 400 })
619
+ }
620
+
621
+ const response = NextResponse.redirect(destination)
616
622
 
617
623
  response.cookies.set({
618
624
  value: token,
@@ -634,7 +640,13 @@ export async function GET(request) {
634
640
  const token = request.nextUrl.searchParams.get('session_token')
635
641
  const redirectUrl = request.nextUrl.searchParams.get('redirect_url')
636
642
 
637
- const response = NextResponse.redirect(new URL(redirectUrl, request.url))
643
+ const destination = new URL(redirectUrl ?? '/', request.url)
644
+ // Prevent open redirects: only allow same-origin destinations
645
+ if (destination.origin !== request.nextUrl.origin) {
646
+ return new Response('Invalid redirect', { status: 400 })
647
+ }
648
+
649
+ const response = NextResponse.redirect(destination)
638
650
 
639
651
  response.cookies.set({
640
652
  value: token,
@@ -27,7 +27,7 @@ CDNs that respect `s-maxage` and `stale-while-revalidate` can cache static and I
27
27
 
28
28
  ### Static assets
29
29
 
30
- Static assets (JavaScript, CSS, images, fonts) served from `/_next/static/` include content hashes in their filenames and have a 1 year `max-age` and `immutable` directive: `public,max-age=31536000,immutable`
30
+ Static assets (JavaScript, CSS, images, fonts) served from `/_next/static/` include content hashes in their filenames and have a 1 year `max-age` and `immutable` directive: `public, max-age=31536000, immutable`
31
31
 
32
32
  You can use [`assetPrefix`](/docs/app/api-reference/config/next-config-js/assetPrefix) to serve static assets from a different domain or CDN origin.
33
33
 
@@ -10,7 +10,7 @@ Next.js includes its own server with `next start` by default. If you have an exi
10
10
 
11
11
  > **Good to know**:
12
12
  >
13
- > - Before deciding to use a custom server, keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements. A custom server will remove important performance optimizations, like **[Automatic Static Optimization](/docs/pages/building-your-application/rendering/automatic-static-optimization).**
13
+ > - Before deciding to use a custom server, keep in mind that it should only be used when the integrated router of Next.js can't meet your app requirements.
14
14
  > - When using standalone output mode, it does not trace custom server files. This mode outputs a separate minimal `server.js` file, instead. These cannot be used together.
15
15
 
16
16
  Take a look at the [following example](https://github.com/vercel/next.js/tree/canary/examples/custom-server) of a custom server:
@@ -9,6 +9,7 @@ related:
9
9
  - app/guides/authentication
10
10
  - app/guides/content-security-policy
11
11
  - app/guides/forms
12
+ - app/guides/server-actions
12
13
  ---
13
14
 
14
15
  [React Server Components](https://react.dev/reference/rsc/server-components) improve performance and simplify data fetching, but also shift where and how data is accessed, changing some of the traditional security assumptions for handling data in frontend apps.
@@ -33,7 +34,7 @@ You should follow a **Zero Trust** model when adopting Server Components in an e
33
34
  import { cookies } from 'next/headers'
34
35
 
35
36
  export default async function Page() {
36
- const cookieStore = cookies()
37
+ const cookieStore = await cookies()
37
38
  const token = cookieStore.get('AUTH_TOKEN')?.value
38
39
 
39
40
  const res = await fetch('https://api.example.com/profile', {
@@ -73,7 +74,8 @@ import { cookies } from 'next/headers'
73
74
  // Component to Server Component which minimizes risk of passing it to a Client
74
75
  // Component.
75
76
  export const getCurrentUser = cache(async () => {
76
- const token = cookies().get('AUTH_TOKEN')
77
+ const cookieStore = await cookies()
78
+ const token = cookieStore.get('AUTH_TOKEN')
77
79
  const decodedToken = await decryptAndValidate(token)
78
80
  // Don't include secret tokens or private information as public fields.
79
81
  // Use classes to avoid accidentally passing the whole object to the client.
@@ -116,12 +118,13 @@ export async function getProfileDTO(slug: string) {
116
118
  ```
117
119
 
118
120
  ```tsx filename="app/page.tsx"
119
- import { getProfile } from '../../data/user'
121
+ import { getProfileDTO } from '../../data/user-dto'
120
122
 
121
- export async function Page({ params: { slug } }) {
123
+ export default async function Page({ params }) {
124
+ const { slug } = await params
122
125
  // This page can now safely pass around this profile knowing
123
126
  // that it shouldn't contain anything sensitive.
124
- const profile = await getProfile(slug);
127
+ const profile = await getProfileDTO(slug)
125
128
  ...
126
129
  }
127
130
  ```
@@ -137,7 +140,8 @@ This approach, however, makes it easier to accidentally expose private data to t
137
140
  ```tsx filename="app/page.tsx"
138
141
  import Profile from './components/profile.tsx'
139
142
 
140
- export async function Page({ params: { slug } }) {
143
+ export default async function Page({ params }) {
144
+ const { slug } = await params
141
145
  const [rows] = await sql`SELECT * FROM user WHERE slug = ${slug}`
142
146
  const userData = rows[0]
143
147
  // EXPOSED: This exposes all the fields in userData to the client because
@@ -184,10 +188,11 @@ import { getUser } from '../data/user'
184
188
  import Profile from './ui/profile'
185
189
 
186
190
  export default async function Page({
187
- params: { slug },
191
+ params,
188
192
  }: {
189
- params: { slug: string }
193
+ params: Promise<{ slug: string }>
190
194
  }) {
195
+ const { slug } = await params
191
196
  const publicProfile = await getUser(slug)
192
197
  return <Profile user={publicProfile} />
193
198
  }
@@ -307,7 +312,7 @@ You should always validate input from client, as they can be easily modified. Fo
307
312
  ```tsx filename="app/page.tsx"
308
313
  // BAD: Trusting searchParams directly
309
314
  export default async function Page({ searchParams }) {
310
- const isAdmin = searchParams.get('isAdmin')
315
+ const isAdmin = (await searchParams).isAdmin
311
316
  if (isAdmin === 'true') {
312
317
  // Vulnerable: relies on untrusted client data
313
318
  return <AdminPanel />
@@ -319,7 +324,8 @@ import { cookies } from 'next/headers'
319
324
  import { verifyAdmin } from './auth'
320
325
 
321
326
  export default async function Page() {
322
- const token = cookies().get('AUTH_TOKEN')
327
+ const cookieStore = await cookies()
328
+ const token = cookieStore.get('AUTH_TOKEN')
323
329
  const isAdmin = await verifyAdmin(token)
324
330
 
325
331
  if (isAdmin) {
@@ -565,8 +571,9 @@ Mutations (e.g. logging out users, updating databases, invalidating caches) shou
565
571
  ```tsx filename="app/page.tsx"
566
572
  // BAD: Triggering a mutation during rendering
567
573
  export default async function Page({ searchParams }) {
568
- if (searchParams.get('logout')) {
569
- cookies().delete('AUTH_TOKEN')
574
+ if ((await searchParams).logout) {
575
+ const cookieStore = await cookies()
576
+ cookieStore.delete('AUTH_TOKEN')
570
577
  }
571
578
 
572
579
  return <UserProfile />