Skip to content

Commit

Permalink
fix: docs for dynamic routing in next 15 (#71531)
Browse files Browse the repository at this point in the history
### What?

Fixes the `params` prop in
https://nextjs.org/docs/canary/app/building-your-application/routing/dynamic-routes

Also fixes a typo: `synchronoulsy` -> `synchronously`
### Why?

There is a breaking change in `next@15.0.0-rc.1`:
https://nextjs.org/blog/next-15-rc2#async-request-apis-breaking-change

Although the api docs have been updated, these have not been.

### How?

updates the docs. (relevant) build and lint passes locally. ran the
prettier fix.

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
  • Loading branch information
SebassNoob and ijjk authored Oct 23, 2024
1 parent 5a54933 commit 96d6a96
Show file tree
Hide file tree
Showing 7 changed files with 28 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@ Dynamic Segments are passed as the `params` prop to [`layout`](/docs/app/api-ref
For example, a blog could include the following route `app/blog/[slug]/page.js` where `[slug]` is the Dynamic Segment for blog posts.

```tsx filename="app/blog/[slug]/page.tsx" switcher
export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const slug = (await params).slug
return <div>My Post: {slug}</div>
}
```

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

Expand All @@ -41,7 +47,11 @@ export default function Page({ params }) {

See the [generateStaticParams()](#generating-static-params) page to learn how to generate the params for the segment.

> **Good to know**: Dynamic Segments are equivalent to [Dynamic Routes](/docs/pages/building-your-application/routing/dynamic-routes) in the `pages` directory.
## Good to know

- Since the `params` prop is a promise. You must use async/await or React's 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 synchronously in Next.js 15, but this behavior will be deprecated in the future.
- Dynamic Segments are equivalent to [Dynamic Routes](/docs/pages/building-your-application/routing/dynamic-routes) in the `pages` directory.

## Generating Static Params

Expand Down Expand Up @@ -105,13 +115,17 @@ The difference between **catch-all** and **optional catch-all** segments is that
When using TypeScript, you can add types for `params` depending on your configured route segment.

```tsx filename="app/blog/[slug]/page.tsx" switcher
export default function Page({ params }: { params: { slug: string } }) {
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
return <h1>My Page</h1>
}
```

```jsx filename="app/blog/[slug]/page.js" switcher
export default function Page({ params }) {
export default async function Page({ params }) {
return <h1>My Page</h1>
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ export default async function Default({ params }) {
| `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.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default async function Layout({ params }) {
| `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.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.

### Root Layouts

Expand Down
4 changes: 2 additions & 2 deletions docs/02-app/02-api-reference/02-file-conventions/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default async function Page({ params }) {
| `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.
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatability, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.

#### `searchParams` (optional)

Expand Down Expand Up @@ -83,7 +83,7 @@ export default async function Page({ searchParams }) {
| `/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.
- In version 14 and earlier, `searchParams` was a synchronous prop. To help with backwards compatability, you can still access it synchronously 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-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.

Expand Down
2 changes: 1 addition & 1 deletion docs/02-app/02-api-reference/04-functions/cookies.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ 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.
- In version 14 and earlier, `cookies` was a synchronous function. To help with backwards compatability, 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/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).
Expand Down
2 changes: 1 addition & 1 deletion docs/02-app/02-api-reference/04-functions/draft-mode.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ 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.
- In version 14 and earlier, `draftMode` was a synchronous function. To help with backwards compatability, you can still access it synchronously 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.

Expand Down
2 changes: 1 addition & 1 deletion docs/02-app/02-api-reference/04-functions/headers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ 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.
- In version 14 and earlier, `headers` was a synchronous function. To help with backwards compatability, you can still access it synchronously 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 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)**.

Expand Down

1 comment on commit 96d6a96

@denu5
Copy link

@denu5 denu5 commented on 96d6a96 Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to also highlight the the client components that are affected by the async params change, as it is futher described in the version 15 migration docs

Usage Patterns for Dynamic Routes

Server Component (Default)

// app/blog/[slug]/page.tsx
export default async function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  return <div>Server Component: {slug}</div>
}

Client Component

// app/blog/[slug]/page.tsx
'use client'
import { use } from 'react'

export default function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = use(params)
  return <div>Client Component: {slug}</div>
}

Temporary Legacy Support

// Not recommended - will be deprecated
'use client'
import { type UnsafeUnwrappedParams } from 'next/headers'

export default function Page({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  // Will show warning in development
  const unsafeParams = params as unknown as UnsafeUnwrappedParams
  return <div>{unsafeParams.slug}</div>
}

The key differences are:

  • Server Components use async/await
  • Client Components use the use hook from React
  • Legacy support requires type casting and will show warnings

Choose the appropriate pattern based on whether your component needs to be client-side (e.g., for interactivity) or can remain a server component (recommended when possible).

Please sign in to comment.