Skip to content

Commit

Permalink
Docs: RC Updates (#70031)
Browse files Browse the repository at this point in the history
**Do not merge.**

- [x] `staticgeneration*` options
- [x] `next/form` now available in pages, `prefetch` prop added to app 
- [x]  ~`next/after` stable~ reverted changes
- [x]  `instrumentation.js` stable
- [x]  More secure server actions
- [x]  Update turbopack docs
    - [x]  https://nextjs.org/docs/architecture/turbopack
- [x] https://nextjs.org/docs/app/api-reference/next-config-js/turbo
    - [x]  Move `turbo` config option out of experimental
- [x]  Document `connection()`
- [x] De-prioritize `unstable_noStore` - add note about using
`connection()`
    - [x]  Replace examples of `noStore` with `connection`
- [x]  Async APIs
    - [x]  Reference Pages
        - [x]  `cookies`
        - [x]  `headers`
        - [x]  `draftMode`
        - [x]  `page.js` (`searchParams` and `params` prop)
        - [x]  `layout.js` (`params` prop)
        - [x]  `route.js` (`params` prop)
        - [x]  `default.js` (`params` prop)
- [x] Clarify `use` usage
https://vercel.slack.com/archives/C06N3AXRQ82/p1726483924090979?thread_ts=1726250070.321229&cid=C06N3AXRQ82
    - [x]  Update **examples** throughout the docs:
        - [x]  `cookies`
        - [x]  `headers`
        - [x]  `draftMode`
        - [x]  `searchParams`
        - [x]  `params`
- [x] Update Dynamic API section in the Server Components Page:
https://nextjs.org/docs/app/building-your-application/rendering/server-components#dynamic-rendering
- [x] Update Dynamic API mentions in Route Handler page:
https://nextjs.org/docs/app/building-your-application/routing/route-handlers#dynamic-functions
- [x] Update upgrade guide for Next 15 RC:
https://nextjs.org/docs/app/building-your-application/upgrading/version-15
- [x] Document codemods for async APIs. PR:
#69572
- [x] Document geo codemod. Done here:
#70064

Closes: https://linear.app/vercel/issue/DOC-3018/nextjs-15-rc-2

---------

Co-authored-by: Rich Haines <hello@richardhaines.dev>
Co-authored-by: Ahmed Abdelbaset <A7med3bdulBaset@gmail.com>
Co-authored-by: Nikhil S <nikhilsnayak3473@gmail.com>
  • Loading branch information
4 people authored Oct 14, 2024
1 parent d6b932d commit 2f6054f
Show file tree
Hide file tree
Showing 53 changed files with 1,088 additions and 592 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -141,11 +141,7 @@ export async function GET() {
}
```

### Dynamic Functions

Route Handlers can be used with dynamic functions from Next.js, like [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers).

#### Cookies
### Cookies

You can read or set cookies with [`cookies`](/docs/app/api-reference/functions/cookies) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function.

Expand All @@ -155,7 +151,7 @@ Alternatively, you can return a new `Response` using the [`Set-Cookie`](https://
import { cookies } from 'next/headers'

export async function GET(request: Request) {
const cookieStore = cookies()
const cookieStore = await cookies()
const token = cookieStore.get('token')

return new Response('Hello, Next.js!', {
Expand All @@ -169,7 +165,7 @@ export async function GET(request: Request) {
import { cookies } from 'next/headers'

export async function GET(request) {
const cookieStore = cookies()
const cookieStore = await cookies()
const token = cookieStore.get('token')

return new Response('Hello, Next.js!', {
Expand All @@ -195,7 +191,7 @@ export async function GET(request) {
}
```

#### Headers
### Headers

You can read headers with [`headers`](/docs/app/api-reference/functions/headers) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function.

Expand All @@ -205,7 +201,7 @@ This `headers` instance is read-only. To set headers, you need to return a new `
import { headers } from 'next/headers'

export async function GET(request: Request) {
const headersList = headers()
const headersList = await headers()
const referer = headersList.get('referer')

return new Response('Hello, Next.js!', {
Expand All @@ -219,7 +215,7 @@ export async function GET(request: Request) {
import { headers } from 'next/headers'

export async function GET(request) {
const headersList = headers()
const headersList = await headers()
const referer = headersList.get('referer')

return new Response('Hello, Next.js!', {
Expand Down Expand Up @@ -272,23 +268,23 @@ Route Handlers can use [Dynamic Segments](/docs/app/building-your-application/ro
```ts filename="app/items/[slug]/route.ts" switcher
export async function GET(
request: Request,
{ params }: { params: { slug: string } }
{ params }: { params: Promise<{ slug: string }> }
) {
const slug = params.slug // 'a', 'b', or 'c'
const slug = (await params).slug // 'a', 'b', or 'c'
}
```

```js filename="app/items/[slug]/route.js" switcher
export async function GET(request, { params }) {
const slug = params.slug // 'a', 'b', or 'c'
const slug = (await params).slug // 'a', 'b', or 'c'
}
```

| Route | Example URL | `params` |
| --------------------------- | ----------- | --------------- |
| `app/items/[slug]/route.js` | `/items/a` | `{ slug: 'a' }` |
| `app/items/[slug]/route.js` | `/items/b` | `{ slug: 'b' }` |
| `app/items/[slug]/route.js` | `/items/c` | `{ slug: 'c' }` |
| Route | Example URL | `params` |
| --------------------------- | ----------- | ------------------------ |
| `app/items/[slug]/route.js` | `/items/a` | `Promise<{ slug: 'a' }>` |
| `app/items/[slug]/route.js` | `/items/b` | `Promise<{ slug: 'b' }>` |
| `app/items/[slug]/route.js` | `/items/c` | `Promise<{ slug: 'c' }>` |

### URL Query Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -129,15 +129,15 @@ export default async function Page() {
}
```

If you are not using any [dynamic functions](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in this route, it will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration).
If you are not using any [Dynamic APIs](/docs/app/building-your-application/rendering/server-components#dynamic-rendering) anywhere else in this route, it will be prerendered during `next build` to a static page. The data can then be updated using [Incremental Static Regeneration](/docs/app/building-your-application/data-fetching/incremental-static-regeneration).

To prevent the page from prerendering, you can add the following to your file:

```js
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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -838,14 +838,16 @@ You can `get`, `set`, and `delete` cookies inside a Server Action using the [`co
import { cookies } from 'next/headers'

export async function exampleAction() {
const cookieStore = await cookies()

// Get cookie
const value = cookies().get('name')?.value
cookieStore.get('name')?.value

// Set cookie
cookies().set('name', 'Delba')
cookieStore.set('name', 'Delba')

// Delete cookie
cookies().delete('name')
cookieStore.delete('name')
}
```
Expand All @@ -856,23 +858,49 @@ import { cookies } from 'next/headers'

export async function exampleAction() {
// Get cookie
const value = cookies().get('name')?.value
const cookieStore = await cookies()

// Get cookie
cookieStore.get('name')?.value

// Set cookie
cookies().set('name', 'Delba')
cookieStore.set('name', 'Delba')

// Delete cookie
cookies().delete('name')
cookieStore.delete('name')
}
```
See [additional examples](/docs/app/api-reference/functions/cookies#deleting-cookies) for deleting cookies from Server Actions.
## Security
By default, when a Server Action is created and exported, it creates a public HTTP endpoint
and should be treated with the same security assumptions and authorization checks. This means, even if a Server Action or utility function is not imported elsewhere in your code, it’s still a publicly accessible.
To improve security, Next.js has the following built-in features:
- **Dead code elimination:** Unused Server Actions won’t have their IDs exposed to the client-side JavaScript bundle, reducing bundle size and improving performance.
- **Secure action IDs:** Next.js creates unguessable, non-deterministic IDs to allow the client to reference and call the Server Action. These IDs are periodically recalculated for enhanced security.
```jsx
// app/actions.js
'use server'

// This action **is** used in our application, so Next.js
// will create a secure ID to allow the client to reference
// and call the Server Action.
export async function updateUserAction(formData) {}

// This action **is not** used in our application, so Next.js
// will automatically remove this code during `next build`
// and will not create a public endpoint.
export async function deleteUserAction(formData) {}
```
### Authentication and authorization
You should treat Server Actions as you would public-facing API endpoints, and ensure that the user is authorized to perform the action. For example:
You should ensure that the user is authorized to perform the action. For example:
```tsx filename="app/actions.ts"
'use server'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -86,30 +82,30 @@ Dynamic rendering is useful when a route has data that is personalized to the us
#### Switching to Dynamic Rendering

During rendering, if a [dynamic function](#dynamic-functions) or uncached data request is discovered, Next.js will switch to dynamically rendering the whole route. This table summarizes how dynamic functions and data caching affect whether a route is statically or dynamically rendered:
During rendering, if a [Dynamic API](#dynamic-apis) or uncached data request is discovered, Next.js will switch to dynamically rendering the whole route. This table summarizes how Dynamic APIs and data caching affect whether a route is statically or dynamically rendered:

| Dynamic Functions | Data | Route |
| ----------------- | ---------- | -------------------- |
| No | Cached | Statically Rendered |
| Yes | Cached | Dynamically Rendered |
| No | Not Cached | Dynamically Rendered |
| Yes | Not Cached | Dynamically Rendered |
| Dynamic APIs | Data | Route |
| ------------ | ---------- | -------------------- |
| No | Cached | Statically Rendered |
| Yes | Cached | Dynamically Rendered |
| No | Not Cached | Dynamically Rendered |
| Yes | Not Cached | Dynamically Rendered |

In the table above, for a route to be fully static, all data must be cached. However, you can have a dynamically rendered route that uses both cached and uncached data fetches.

As a developer, you do not need to choose between static and dynamic rendering as Next.js will automatically choose the best rendering strategy for each route based on the features and APIs used. Instead, you choose when to [cache](/docs/app/building-your-application/data-fetching/fetching) or [revalidate specific data](/docs/app/building-your-application/data-fetching/incremental-static-regeneration), and you may choose to [stream](#streaming) parts of your UI.

#### Dynamic Functions

Dynamic functions rely on information that can only be known at request time such as a user's cookies, current requests headers, or the URL's search params. In Next.js, these dynamic APIs are:
### Dynamic APIs

- [`cookies()`](/docs/app/api-reference/functions/cookies)
- [`headers()`](/docs/app/api-reference/functions/headers)
- [`unstable_noStore()`](/docs/app/api-reference/functions/unstable_noStore)
- [`unstable_after()`](/docs/app/api-reference/functions/unstable_after):
- [`searchParams` prop](/docs/app/api-reference/file-conventions/page#searchparams-optional)
Dynamic APIs rely on information that can only be known at request time (and not ahead of time during prerendering). Using any of these APIs signals the developer's intention and will opt the whole route into dynamic rendering at the request time. These APIs include:

Using any of these functions will opt the whole route into dynamic rendering at request time.
- [`cookies`](https://www.notion.so/docs/app/api-reference/functions/cookies)
- [`headers`](https://www.notion.so/docs/app/api-reference/functions/headers)
- [`unstable_noStore`](https://www.notion.so/docs/app/api-reference/functions/unstable_noStore)
- [`unstable_after`](https://www.notion.so/docs/app/api-reference/functions/unstable_after)
- [`connection`](https://www.notion.so/docs/app/api-reference/functions/connection)
- [`draftMode`](https://www.notion.so/docs/app/api-reference/functions/draft-mode)
- [`searchParams` prop](https://www.notion.so/docs/app/api-reference/file-conventions/page#searchparams-optional)

### Streaming

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,24 +127,24 @@ module.exports = nextConfig

## Dynamic Components

When creating the prerender for your route during `next build`, Next.js requires that dynamic functions are wrapped with React Suspense. The `fallback` is then included in the prerender.
When creating the prerender for your route during `next build`, Next.js requires that Dynamic APIs are wrapped with React Suspense. The `fallback` is then included in the prerender.

For example, using functions like `cookies()` or `headers()`:
For example, using functions like `cookies` or `headers`:

```jsx filename="app/user.js" switcher
import { cookies } from 'next/headers'

export function User() {
const session = cookies().get('session')?.value
export async function User() {
const session = (await cookies()).get('session')?.value
return '...'
}
```
```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 '...'
}
```
Expand Down Expand Up @@ -224,17 +224,21 @@ export default function Page({ searchParams }) {
Inside of the table component, accessing the value from `searchParams` will make the component run dynamically:
```tsx filename="app/table.tsx" switcher
export function Table({ searchParams }: { searchParams: { sort: string } }) {
const sort = searchParams.sort === 'true'
export async function Table({
searchParams,
}: {
searchParams: Promise<{ sort: string }>
}) {
const sort = (await searchParams).sort === 'true'
return '...'
}
```
```jsx filename="app/table.js" switcher
export function Table({ searchParams }: {
searchParams: { sort: string }
export async function Table({ searchParams }: {
searchParams: Promise<{ sort: string }>
}) {
const sort = searchParams.sort === 'true';
const sort = (await searchParams).sort === 'true';
return '...'
}
```
Loading

0 comments on commit 2f6054f

Please sign in to comment.