diff --git a/docs/01-app/01-getting-started/03-layouts-and-pages.mdx b/docs/01-app/01-getting-started/03-layouts-and-pages.mdx index b3c5d4664d371..d58d445c0e5a5 100644 --- a/docs/01-app/01-getting-started/03-layouts-and-pages.mdx +++ b/docs/01-app/01-getting-started/03-layouts-and-pages.mdx @@ -324,3 +324,37 @@ export default async function Post({ post }) { ``` > **Good to know**: `` 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. + +## Route Props Helpers + +Next.js exposes utility types that infer `params` and named slots from your route structure: + +- [**PageProps**](/docs/app/api-reference/file-conventions/page#page-props-helper): Props for `page` components, including `params` and `searchParams`. +- [**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`). + +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). + +```tsx filename="app/blog/[slug]/page.tsx" +export default async function Page(props: PageProps<'/blog/[slug]'>) { + const { slug } = await props.params + return

Blog post: {slug}

+} +``` + +```tsx filename="app/dashboard/layout.tsx" +export default function Layout(props: LayoutProps<'/dashboard'>) { + return ( +
+ {props.children} + {/* If you have app/dashboard/@analytics, it appears as a typed slot: */} + {/* {props.analytics} */} +
+ ) +} +``` + +> **Good to know** +> +> - Static routes resolve `params` to `{}`. +> - `PageProps`, `LayoutProps` are global helpers — no imports required. +> - Types are generated during `next dev`, `next build` or `next typegen`. diff --git a/docs/01-app/01-getting-started/15-route-handlers-and-middleware.mdx b/docs/01-app/01-getting-started/15-route-handlers-and-middleware.mdx index a65c07ba285cb..aaf276c16774c 100644 --- a/docs/01-app/01-getting-started/15-route-handlers-and-middleware.mdx +++ b/docs/01-app/01-getting-started/15-route-handlers-and-middleware.mdx @@ -126,6 +126,23 @@ export async function POST(request) {} 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). +### Route Context Helper + +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: + +```ts filename="app/users/[id]/route.ts" switcher +import type { NextRequest } from 'next/server' + +export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) { + const { id } = await ctx.params + return Response.json({ id }) +} +``` + +> **Good to know** +> +> - Types are generated during `next dev`, `next build` or `next typegen`. + ## Middleware 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. diff --git a/docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.mdx b/docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.mdx index 3490c43610411..2c6f9581fc8be 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.mdx @@ -111,7 +111,7 @@ The difference between **catch-all** and **optional catch-all** segments is that ### TypeScript -When using TypeScript, you can add types for `params` depending on your configured route segment. +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. | Route | `params` Type Definition | | ----------------------------------- | ---------------------------------------- | diff --git a/docs/01-app/03-api-reference/03-file-conventions/layout.mdx b/docs/01-app/03-api-reference/03-file-conventions/layout.mdx index b327346d4470c..36da2f45c0739 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/layout.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/layout.mdx @@ -86,6 +86,26 @@ export default async function Layout({ children, params }) { - 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 compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future. +### Layout Props Helper + +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. + +```tsx filename="app/dashboard/layout.tsx" switcher +export default function Layout(props: LayoutProps<'/dashboard'>) { + return ( +
+ {props.children} + {/* If you have app/dashboard/@analytics, it appears as a typed slot: */} + {/* {props.analytics} */} +
+ ) +} +``` + +> **Good to know**: +> +> - Types are generated during `next dev`, `next build` or `next typegen`. + ### Root Layout 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`. diff --git a/docs/01-app/03-api-reference/03-file-conventions/page.mdx b/docs/01-app/03-api-reference/03-file-conventions/page.mdx index bc3c6e08f92cb..ca85857d3f42c 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/page.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/page.mdx @@ -118,6 +118,24 @@ export default function Page({ searchParams }) { - `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. - `searchParams` is a plain JavaScript object, not a `URLSearchParams` instance. +### Page Props Helper + +You can type pages with `PageProps` to get strongly typed `params` and `searchParams` from the route literal. `PageProps` is a globally available helper. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export default async function Page(props: PageProps<'/blog/[slug]'>) { + const { slug } = await props.params + const query = await props.searchParams + return

Blog Post: {slug}

+} +``` + +> **Good to know** +> +> - Using a literal route (e.g. `'/blog/[slug]'`) enables autocomplete and strict keys for `params`. +> - Static routes resolve `params` to `{}`. +> - Types are generated during `next dev`, `next build`, or with `next typegen`. + ## Examples ### Displaying content based on `params` diff --git a/docs/01-app/03-api-reference/03-file-conventions/route.mdx b/docs/01-app/03-api-reference/03-file-conventions/route.mdx index 54728758f8465..9ebac33682af6 100644 --- a/docs/01-app/03-api-reference/03-file-conventions/route.mdx +++ b/docs/01-app/03-api-reference/03-file-conventions/route.mdx @@ -102,6 +102,23 @@ export async function GET(request, { params }) { | `app/shop/[tag]/[item]/route.js` | `/shop/1/2` | `Promise<{ tag: '1', item: '2' }>` | | `app/blog/[...slug]/route.js` | `/blog/1/2` | `Promise<{ slug: ['1', '2'] }>` | +### Route Context Helper + +You can type the Route Handler context using `RouteContext` to get strongly typed `params` from a route literal. `RouteContext` is a globally available helper. + +```ts filename="app/users/[id]/route.ts" switcher +import type { NextRequest } from 'next/server' + +export async function GET(_req: NextRequest, ctx: RouteContext<'/users/[id]'>) { + const { id } = await ctx.params + return Response.json({ id }) +} +``` + +> **Good to know** +> +> - Types are generated during `next dev`, `next build` or `next typegen`. + ## Examples ### Cookies diff --git a/docs/01-app/03-api-reference/04-functions/generate-metadata.mdx b/docs/01-app/03-api-reference/04-functions/generate-metadata.mdx index 51e24dee3be05..8e5ec420bb1eb 100644 --- a/docs/01-app/03-api-reference/04-functions/generate-metadata.mdx +++ b/docs/01-app/03-api-reference/04-functions/generate-metadata.mdx @@ -95,6 +95,8 @@ export async function generateMetadata({ params, searchParams }, parent) { export default function Page({ params, searchParams }) {} ``` +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. + > **Good to know**: > > - Metadata can be added to `layout.js` and `page.js` files. diff --git a/docs/01-app/03-api-reference/04-functions/generate-static-params.mdx b/docs/01-app/03-api-reference/04-functions/generate-static-params.mdx index dff0404729b3d..a46c0855f6cd7 100644 --- a/docs/01-app/03-api-reference/04-functions/generate-static-params.mdx +++ b/docs/01-app/03-api-reference/04-functions/generate-static-params.mdx @@ -436,6 +436,26 @@ export default function Page({ params }) { } ``` +Notice that the params argument can be accessed synchronously and includes only parent segment params. + +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): + +```ts filename="app/products/[category]/[product]/page.tsx" switcher +export async function generateStaticParams({ + params: { category }, +}: { + params: Awaited['params']> +}) { + const products = await fetch( + `https://.../products?category=${category}` + ).then((res) => res.json()) + + return products.map((product) => ({ + product: product.id, + })) +} +``` + > **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. ## Version History diff --git a/docs/01-app/03-api-reference/04-functions/generate-viewport.mdx b/docs/01-app/03-api-reference/04-functions/generate-viewport.mdx index d79352f4a3f5b..ff888d073e304 100644 --- a/docs/01-app/03-api-reference/04-functions/generate-viewport.mdx +++ b/docs/01-app/03-api-reference/04-functions/generate-viewport.mdx @@ -50,6 +50,8 @@ export function generateViewport({ params }) { } ``` +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. + ```jsx filename="layout.js | page.js" switcher export function generateViewport({ params }) { return { diff --git a/docs/01-app/03-api-reference/05-config/02-typescript.mdx b/docs/01-app/03-api-reference/05-config/02-typescript.mdx index b7513067867ca..18d1eeeb10239 100644 --- a/docs/01-app/03-api-reference/05-config/02-typescript.mdx +++ b/docs/01-app/03-api-reference/05-config/02-typescript.mdx @@ -70,6 +70,14 @@ export default async function Page() { 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. +## Route-Aware Type Helpers + +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): + +- [`PageProps`](/docs/app/api-reference/file-conventions/page#page-props-helper) +- [`LayoutProps`](/docs/app/api-reference/file-conventions/layout#layout-props-helper) +- [`RouteContext`](/docs/app/api-reference/file-conventions/route#route-context-helper) + ## Examples @@ -139,6 +147,28 @@ import Link from 'next/link' ``` +The same applies for redirecting routes defined by middleware: + +```ts filename="middleware.ts" +import { NextRequest, NextResponse } from 'next/server' + +export function middleware(request: NextRequest) { + if (request.nextUrl.pathname === '/middleware-redirect') { + return NextResponse.redirect(new URL('/', request.url)) + } + + return NextResponse.next() +} +``` + +```tsx filename="app/some/page.tsx" +import type { Route } from 'next' + +export default function Page() { + return Link Text +} +``` + To accept `href` in a custom component wrapping `next/link`, use a generic: ```tsx diff --git a/docs/01-app/03-api-reference/06-cli/next.mdx b/docs/01-app/03-api-reference/06-cli/next.mdx index c4ad60b552522..00024baaaa99e 100644 --- a/docs/01-app/03-api-reference/06-cli/next.mdx +++ b/docs/01-app/03-api-reference/06-cli/next.mdx @@ -34,6 +34,7 @@ The following commands are available: | [`info`](#next-info-options) | Prints relevant details about the current system which can be used to report Next.js bugs. | | [`lint`](#next-lint-options) | Runs ESLint for all files in the `/src`, `/app`, `/pages`, `/components`, and `/lib` directories. It also provides a guided setup to install any required dependencies if ESLint it is not already configured in your application. | | [`telemetry`](#next-telemetry-options) | Allows you to enable or disable Next.js' completely anonymous telemetry collection. | +| [`typegen`](#next-typegen-options) | Generates TypeScript definitions for routes, pages, layouts, and route handlers without running a full build. | > **Good to know**: Running `next` without a command is an alias for `next dev`. @@ -138,6 +139,8 @@ The following options are available for the `next info` command: ### `next lint` options +> **Warning**: This option is deprecated and will be removed in Next 16. A [codemod](/blog/next-15-5#next-lint-deprecation) is available to migrate to ESLint CLI. + `next lint` runs ESLint for all files in the `pages/`, `app/`, `components/`, `lib/`, and `src/` directories. It also provides a guided setup to install any required dependencies if ESLint is not already configured in your application. The following options are available for the `next lint` command: @@ -182,6 +185,35 @@ The following options are available for the `next telemetry` command: Learn more about [Telemetry](/telemetry). +### `next typegen` Options + +`next typegen` generates TypeScript definitions for your application's routes without performing a full build. This is useful for IDE autocomplete and CI type-checking of route usage. + +Previously, route types were only generated during `next dev` or `next build`, which meant running `tsc --noEmit` directly wouldn't validate your route types. Now you can generate types independently and validate them externally: + +```bash filename="Terminal" +# Generate route types first, then validate with TypeScript +next typegen && tsc --noEmit + +# Or in CI workflows for type checking without building +next typegen && npm run type-check +``` + +The following options are available for the `next typegen` command: + +| Option | Description | +| ------------- | -------------------------------------------------------------------------------------------- | +| `-h, --help` | Show all available options. | +| `[directory]` | A directory on which to generate types. If not provided, the current directory will be used. | + +Output files are written to `/types` (default: `.next/types`): + +```bash filename="Terminal" +next typegen +# or for a specific app +next typegen ./apps/web +``` + ## Examples ### Debugging prerender errors @@ -264,4 +296,5 @@ NODE_OPTIONS='--inspect' next | Version | Changes | | --------- | ------------------------------------------------------------------------------- | +| `v15.5.0` | Add the `next typegen` command | | `v15.4.0` | Add `--debug-prerender` option for `next build` to help debug prerender errors. | diff --git a/docs/02-pages/02-guides/amp.mdx b/docs/02-pages/02-guides/amp.mdx index ff05318a467f6..051a3e123afb8 100644 --- a/docs/02-pages/02-guides/amp.mdx +++ b/docs/02-pages/02-guides/amp.mdx @@ -11,7 +11,7 @@ description: With minimal config, and without leaving React, you can start addin -> **Warning**: Built-in amp support will be removed in Next.js 16. +> **Warning**: Built-in AMP support will be removed in Next.js 16. With Next.js you can turn any React page into an AMP page, with minimal config, and without leaving React. diff --git a/docs/02-pages/04-api-reference/03-functions/use-amp.mdx b/docs/02-pages/04-api-reference/03-functions/use-amp.mdx index 849205307b7c7..6237a4fb88da1 100644 --- a/docs/02-pages/04-api-reference/03-functions/use-amp.mdx +++ b/docs/02-pages/04-api-reference/03-functions/use-amp.mdx @@ -10,7 +10,7 @@ description: Enable AMP in a page, and control the way Next.js adds AMP to the p -> AMP support is one of our advanced features, you can [read more about AMP here](/docs/pages/guides/amp). +> **Warning**: Built-in AMP support will be removed in Next.js 16. To enable AMP, add the following config to your page: