Skip to content

Commit 533bcd1

Browse files
icyJosephbgubijjk
authored
docs: Route props helpers, typegen and next lint deprecation (#82784)
As this lands and people learn about the new Route Props helpers, I think we will have this period where the TypeScript snippets show the manual typing, but in key places we introduce the helpers usage. We can then follow up updating all TypeScript snippets showing a page, layout or route, to use these. --------- Co-authored-by: Ben Gubler <[email protected]> Co-authored-by: JJ Kasper <[email protected]>
1 parent e6718d6 commit 533bcd1

File tree

13 files changed

+196
-3
lines changed

13 files changed

+196
-3
lines changed

docs/01-app/01-getting-started/03-layouts-and-pages.mdx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,37 @@ export default async function Post({ post }) {
324324
```
325325

326326
> **Good to know**: `<Link>` is the primary way to navigate between routes in Next.js. You can also use the [`useRouter` hook](/docs/app/api-reference/functions/use-router) for more advanced navigation.
327+
328+
## Route Props Helpers
329+
330+
Next.js exposes utility types that infer `params` and named slots from your route structure:
331+
332+
- [**PageProps**](/docs/app/api-reference/file-conventions/page#page-props-helper): Props for `page` components, including `params` and `searchParams`.
333+
- [**LayoutProps**](/docs/app/api-reference/file-conventions/layout#layout-props-helper): Props for `layout` components, including `children` and any named slots (e.g. folders like `@analytics`).
334+
335+
These are globally available helpers, generated when running either `next dev`, `next build` or [`next typegen`](/docs/app/api-reference/cli/next#next-typegen-options).
336+
337+
```tsx filename="app/blog/[slug]/page.tsx"
338+
export default async function Page(props: PageProps<'/blog/[slug]'>) {
339+
const { slug } = await props.params
340+
return <h1>Blog post: {slug}</h1>
341+
}
342+
```
343+
344+
```tsx filename="app/dashboard/layout.tsx"
345+
export default function Layout(props: LayoutProps<'/dashboard'>) {
346+
return (
347+
<section>
348+
{props.children}
349+
{/* If you have app/dashboard/@analytics, it appears as a typed slot: */}
350+
{/* {props.analytics} */}
351+
</section>
352+
)
353+
}
354+
```
355+
356+
> **Good to know**
357+
>
358+
> - Static routes resolve `params` to `{}`.
359+
> - `PageProps`, `LayoutProps` are global helpers — no imports required.
360+
> - Types are generated during `next dev`, `next build` or `next typegen`.

docs/01-app/01-getting-started/15-route-handlers-and-middleware.mdx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,23 @@ export async function POST(request) {}
126126

127127
Read more about how Route Handlers [complement your frontend application](/docs/app/guides/backend-for-frontend), or explore the Route Handlers [API Reference](/docs/app/api-reference/file-conventions/route).
128128

129+
### Route Context Helper
130+
131+
In TypeScript, you can type the `context` parameter for Route Handlers with the globally available [`RouteContext`](/docs/app/api-reference/file-conventions/route#route-context-helper) helper:
132+
133+
```ts filename="app/users/[id]/route.ts" switcher
134+
import type { NextRequest } from 'next/server'
135+
136+
export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) {
137+
const { id } = await ctx.params
138+
return Response.json({ id })
139+
}
140+
```
141+
142+
> **Good to know**
143+
>
144+
> - Types are generated during `next dev`, `next build` or `next typegen`.
145+
129146
## Middleware
130147

131148
Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.

docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ The difference between **catch-all** and **optional catch-all** segments is that
111111

112112
### TypeScript
113113

114-
When using TypeScript, you can add types for `params` depending on your configured route segment.
114+
When using TypeScript, you can add types for `params` depending on your configured route segment — use [`PageProps<'/route'>`](/docs/app/api-reference/file-conventions/page#page-props-helper), [`LayoutProps<'/route'>`](/docs/app/api-reference/file-conventions/layout#layout-props-helper), or [`RouteContext<'/route'>`](/docs/app/api-reference/file-conventions/route#route-context-helper) to type `params` in `page`, `layout`, and `route` respectively.
115115

116116
| Route | `params` Type Definition |
117117
| ----------------------------------- | ---------------------------------------- |

docs/01-app/03-api-reference/03-file-conventions/layout.mdx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,26 @@ export default async function Layout({ children, params }) {
8686
- 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.
8787
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
8888

89+
### Layout Props Helper
90+
91+
You can type layouts with `LayoutProps` to get a strongly typed `params` and named slots inferred from your directory structure. `LayoutProps` is a globally available helper.
92+
93+
```tsx filename="app/dashboard/layout.tsx" switcher
94+
export default function Layout(props: LayoutProps<'/dashboard'>) {
95+
return (
96+
<section>
97+
{props.children}
98+
{/* If you have app/dashboard/@analytics, it appears as a typed slot: */}
99+
{/* {props.analytics} */}
100+
</section>
101+
)
102+
}
103+
```
104+
105+
> **Good to know**:
106+
>
107+
> - Types are generated during `next dev`, `next build` or `next typegen`.
108+
89109
### Root Layout
90110

91111
The `app` directory **must** include a **root layout**, which is the top-most layout in the root `app` directory. Typically, the root layout is `app/layout.js`.

docs/01-app/03-api-reference/03-file-conventions/page.mdx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,24 @@ export default function Page({ searchParams }) {
118118
- `searchParams` is a **[Dynamic API](/docs/app/getting-started/partial-prerendering#dynamic-rendering)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/getting-started/partial-prerendering#dynamic-rendering)** at request time.
119119
- `searchParams` is a plain JavaScript object, not a `URLSearchParams` instance.
120120

121+
### Page Props Helper
122+
123+
You can type pages with `PageProps` to get strongly typed `params` and `searchParams` from the route literal. `PageProps` is a globally available helper.
124+
125+
```tsx filename="app/blog/[slug]/page.tsx" switcher
126+
export default async function Page(props: PageProps<'/blog/[slug]'>) {
127+
const { slug } = await props.params
128+
const query = await props.searchParams
129+
return <h1>Blog Post: {slug}</h1>
130+
}
131+
```
132+
133+
> **Good to know**
134+
>
135+
> - Using a literal route (e.g. `'/blog/[slug]'`) enables autocomplete and strict keys for `params`.
136+
> - Static routes resolve `params` to `{}`.
137+
> - Types are generated during `next dev`, `next build`, or with `next typegen`.
138+
121139
## Examples
122140

123141
### Displaying content based on `params`

docs/01-app/03-api-reference/03-file-conventions/route.mdx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,23 @@ export async function GET(request, { params }) {
102102
| `app/shop/[tag]/[item]/route.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` |
103103
| `app/blog/[...slug]/route.js` | `/blog/1/2` | `Promise<{ slug: ['1', '2'] }>` |
104104

105+
### Route Context Helper
106+
107+
You can type the Route Handler context using `RouteContext` to get strongly typed `params` from a route literal. `RouteContext` is a globally available helper.
108+
109+
```ts filename="app/users/[id]/route.ts" switcher
110+
import type { NextRequest } from 'next/server'
111+
112+
export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) {
113+
const { id } = await ctx.params
114+
return Response.json({ id })
115+
}
116+
```
117+
118+
> **Good to know**
119+
>
120+
> - Types are generated during `next dev`, `next build` or `next typegen`.
121+
105122
## Examples
106123

107124
### Cookies

docs/01-app/03-api-reference/04-functions/generate-metadata.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export async function generateMetadata({ params, searchParams }, parent) {
9595
export default function Page({ params, searchParams }) {}
9696
```
9797
98+
For type completion of `params` and `searchParams`, you can type the first argument with [`PageProps<'/route'>`](/docs/app/api-reference/file-conventions/page#page-props-helper) or [`LayoutProps<'/route'>`](/docs/app/api-reference/file-conventions/layout#layout-props-helper) for pages and layouts respectively.
99+
98100
> **Good to know**:
99101
>
100102
> - Metadata can be added to `layout.js` and `page.js` files.

docs/01-app/03-api-reference/04-functions/generate-static-params.mdx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,26 @@ export default function Page({ params }) {
436436
}
437437
```
438438

439+
Notice that the params argument can be accessed synchronously and includes only parent segment params.
440+
441+
For type completion, you can make use of the TypeScript `Awaited` helper in combination with either [`Page Props helper`](/docs/app/api-reference/file-conventions/page#page-props-helper) or [`Layout Props helper`](/docs/app/api-reference/file-conventions/layout#layout-props-helper):
442+
443+
```ts filename="app/products/[category]/[product]/page.tsx" switcher
444+
export async function generateStaticParams({
445+
params: { category },
446+
}: {
447+
params: Awaited<LayoutProps<'/products/[category]'>['params']>
448+
}) {
449+
const products = await fetch(
450+
`https://.../products?category=${category}`
451+
).then((res) => res.json())
452+
453+
return products.map((product) => ({
454+
product: product.id,
455+
}))
456+
}
457+
```
458+
439459
> **Good to know**: `fetch` requests are automatically [memoized](/docs/app/guides/caching#request-memoization) for the same data across all `generate`-prefixed functions, Layouts, Pages, and Server Components. React [`cache` can be used](/docs/app/guides/caching#react-cache-function) if `fetch` is unavailable.
440460
441461
## Version History

docs/01-app/03-api-reference/04-functions/generate-viewport.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ export function generateViewport({ params }) {
5050
}
5151
```
5252

53+
In TypeScript, the `params` argument can be typed via [`PageProps<'/route'>`](/docs/app/api-reference/file-conventions/page#page-props-helper) or [`LayoutProps<'/route'>`](/docs/app/api-reference/file-conventions/layout#layout-props-helper) depending on where `generateViewport` is defined.
54+
5355
```jsx filename="layout.js | page.js" switcher
5456
export function generateViewport({ params }) {
5557
return {

docs/01-app/03-api-reference/05-config/02-typescript.mdx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ export default async function Page() {
7070

7171
For _complete_ end-to-end type safety, this also requires your database or content provider to support TypeScript. This could be through using an [ORM](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) or type-safe query builder.
7272

73+
## Route-Aware Type Helpers
74+
75+
Next.js generates global helpers for App Router route types. These are available without imports and are generated during `next dev`, `next build`, or via [`next typegen`](/docs/app/api-reference/cli/next#next-typegen-options):
76+
77+
- [`PageProps`](/docs/app/api-reference/file-conventions/page#page-props-helper)
78+
- [`LayoutProps`](/docs/app/api-reference/file-conventions/layout#layout-props-helper)
79+
- [`RouteContext`](/docs/app/api-reference/file-conventions/route#route-context-helper)
80+
7381
</AppOnly>
7482

7583
## Examples
@@ -139,6 +147,28 @@ import Link from 'next/link'
139147
<Link href="/aboot" />
140148
```
141149

150+
The same applies for redirecting routes defined by middleware:
151+
152+
```ts filename="middleware.ts"
153+
import { NextRequest, NextResponse } from 'next/server'
154+
155+
export function middleware(request: NextRequest) {
156+
if (request.nextUrl.pathname === '/middleware-redirect') {
157+
return NextResponse.redirect(new URL('/', request.url))
158+
}
159+
160+
return NextResponse.next()
161+
}
162+
```
163+
164+
```tsx filename="app/some/page.tsx"
165+
import type { Route } from 'next'
166+
167+
export default function Page() {
168+
return <Link href={'/middleware-redirect' as Route}>Link Text</Link>
169+
}
170+
```
171+
142172
To accept `href` in a custom component wrapping `next/link`, use a generic:
143173

144174
```tsx

0 commit comments

Comments
 (0)