diff --git a/docs/01-app/01-getting-started/06-cache-components.mdx b/docs/01-app/01-getting-started/06-cache-components.mdx
index 54d13ded1d206..24ea804e650ad 100644
--- a/docs/01-app/01-getting-started/06-cache-components.mdx
+++ b/docs/01-app/01-getting-started/06-cache-components.mdx
@@ -158,7 +158,7 @@ A specific type of dynamic data that requires request context, only available wh
- [`cookies()`](/docs/app/api-reference/functions/cookies) - User's cookie data
- [`headers()`](/docs/app/api-reference/functions/headers) - Request headers
- [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) - URL query parameters
-- [`params`](/docs/app/api-reference/file-conventions/page#params-optional) - Dynamic route parameters (unless at least one sample is provided via [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params))
+- [`params`](/docs/app/api-reference/file-conventions/page#params-optional) - Dynamic route parameters (unless at least one sample is provided via [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params)). See [Dynamic Routes with Cache Components](/docs/app/api-reference/file-conventions/dynamic-routes#with-cache-components) for detailed patterns.
```tsx filename="page.tsx"
import { cookies, headers } from 'next/headers'
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 27e84fe4fd17f..418960077208d 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
@@ -145,6 +145,121 @@ export default async function Page(props: PageProps<'/[locale]'>) {
- 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 compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
+### With Cache Components
+
+When using [Cache Components](/docs/app/getting-started/cache-components) with dynamic routes, params are runtime data that require special handling. During prerendering, Next.js doesn't know which params users will request (like `/blog/hello-world` or `/blog/123`), so you need to provide fallback UI using `` boundaries.
+
+You should use [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) to prerender your most popular routes at build time (e.g., `/blog/1`, `/blog/2`, `/blog/3`). This serves two purposes: it validates your route doesn't incorrectly access dynamic APIs like `cookies()` or `headers()`, and it creates static HTML files for instant loading of those specific routes.
+
+When you use `generateStaticParams`, Next.js also generates a static shell where params are treated as dynamic. When users request params not prerendered at build time, Next.js immediately sends this static shell (containing content like headers, navigation, and layout), while param-dependent content updates the UI when it's ready. This provides a better user experience than waiting for the entire page to render.
+
+The sections below show different patterns, from simplest (all runtime) to most optimized (prerendered samples, and caching). We recommend wrapping param access in Suspense boundaries to provide meaningful loading states.
+
+> **Good to know**: You can check the `X-Nextjs-Cache` response header to verify your caching strategy. It will show `HIT` or `MISS` for disk-cached routes, and won't appear for memory-only caching. Your hosting provider might replace this header with their own version.
+
+#### Without `generateStaticParams`
+
+The simplest approach. All params are runtime data.
+
+- **Build time**: No validation, no prerendering
+- **Prerendered params**: None
+- **Runtime params**: Reading params requires Suspense fallback UI (build fails without it), shell renders on-demand, UI updates when content resolves
+- **Caching**: Memory only (cleared on server restart)
+
+```tsx filename="app/blog/[slug]/page.tsx"
+import { Suspense } from 'react'
+
+export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
+ return (
+
+
Blog Post
+ Loading...
}>
+ p.slug)} />
+
+
+ )
+}
+
+async function Content({ slug }: { slug: Promise }) {
+ const resolvedSlug = await slug
+ return {/* Your content */}
+}
+```
+
+#### With `generateStaticParams`
+
+Enables build-time validation and prerendering of popular routes. Next.js generates both the prerendered param pages and a static shell (where params are treated as dynamic). When a runtime param is encountered, Next.js uses this static shell to render immediately while streaming param-dependent content.
+
+- **Build time**: Validates route, prerenders samples (e.g., `/1`, `/2`, `/3`), generates static shell
+- **Prerendered params**: Instant - served from disk
+- **Runtime params**: Static shell renders immediately, UI updates when param-dependent content resolves
+- **Caching**: Memory only for runtime params (cleared on server restart)
+
+```tsx filename="app/blog/[slug]/page.tsx"
+import { Suspense } from 'react'
+
+export async function generateStaticParams() {
+ return [{ slug: '1' }, { slug: '2' }, { slug: '3' }]
+}
+
+export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
+ return (
+
+
Blog Post
+ Loading...
}>
+ p.slug)} />
+
+
+ )
+}
+
+async function Content({ slug }: { slug: Promise }) {
+ const resolvedSlug = await slug
+ return {/* Your content */}
+}
+```
+
+#### With `generateStaticParams` and `use cache` on page
+
+Provides immediate UI for prerendered and previously visited routes, with disk persistence similar to ISR. When a runtime param is encountered, Next.js uses the static shell if it has visible content, or prerenders the full page (ISR-style) if the shell is empty.
+
+- **Build time**: Validates route, prerenders samples, generates static shell
+- **Prerendered params**: Instant - served from disk
+- **Runtime params**: Static shell renders immediately (if it has content), then streams param-dependent content. Full page is cached to disk after first visit.
+- **Caching**: Disk (persists across server restarts)
+
+```tsx filename="app/blog/[slug]/page.tsx"
+import { Suspense } from 'react'
+import { cacheLife } from 'next/cache'
+
+export async function generateStaticParams() {
+ return [{ slug: '1' }, { slug: '2' }, { slug: '3' }]
+}
+
+export default async function Page({ params }: PageProps<'/blog/[slug]'>) {
+ 'use cache'
+ cacheLife('days')
+ return (
+
+
Blog Post
+ Loading...
}>
+ p.slug)} />
+
+
+ )
+}
+
+async function Content({ slug }: { slug: Promise }) {
+ const resolvedSlug = await slug
+ return {/* Your content */}
+}
+```
+
+> **Good to know**:
+>
+> - **Prefetching**: When navigating via ``, Next.js prefetches the static shell. For runtime params (not in `generateStaticParams`), any content that depends on the param value (such as `use cache` functions after `await params`) is not included in the prefetch and will be fetched during navigation, showing the Suspense fallback until ready.
+> - **Parent Suspense boundaries** (like [`loading.tsx`](/docs/app/api-reference/file-conventions/loading) or a layout Suspense wrapping children) take precedence over page-level `use cache` for runtime params. While build-time params from `generateStaticParams` are still fully cached to disk, runtime params will use the parent fallback UI and stream with memory caching only - no disk files are created.
+
## Examples
### With `generateStaticParams`
@@ -172,3 +287,36 @@ export async function generateStaticParams() {
```
When using `fetch` inside the `generateStaticParams` function, the requests are [automatically deduplicated](/docs/app/guides/caching#request-memoization). This avoids multiple network calls for the same data Layouts, Pages, and other `generateStaticParams` functions, speeding up build time.
+
+### Dynamic GET Route Handlers with `generateStaticParams`
+
+`generateStaticParams` also works with dynamic [Route Handlers](/docs/app/api-reference/file-conventions/route) to statically generate API responses at build time:
+
+```ts filename="app/api/posts/[id]/route.ts" switcher
+export async function generateStaticParams() {
+ return [{ id: '1' }, { id: '2' }, { id: '3' }]
+}
+
+export async function GET(
+ request: Request,
+ { params }: RouteContext<'/api/posts/[id]'>
+) {
+ const { id } = await params
+ // Fetch and return post data
+ return Response.json({ id, title: `Post ${id}` })
+}
+```
+
+```js filename="app/api/posts/[id]/route.js" switcher
+export async function generateStaticParams() {
+ return [{ id: '1' }, { id: '2' }, { id: '3' }]
+}
+
+export async function GET(request, { params }) {
+ const { id } = await params
+ // Fetch and return post data
+ return Response.json({ id, title: `Post ${id}` })
+}
+```
+
+In this example, the route handlers for all blog post IDs (1, 2 and 3) returned by `generateStaticParams` will be statically generated at build time. Requests to other IDs will be handled dynamically at request time.
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 8c3ed020ffbc5..c1f513a1d4ea5 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
@@ -334,6 +334,14 @@ export async function GET(request, { params }) {
| `app/items/[slug]/route.js` | `/items/b` | `Promise<{ slug: 'b' }>` |
| `app/items/[slug]/route.js` | `/items/c` | `Promise<{ slug: 'c' }>` |
+#### Static Generation with `generateStaticParams`
+
+You can use [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) with dynamic Route Handlers to statically generate responses at build time for specified params, while handling other params dynamically at request time.
+
+When using [Cache Components](/docs/app/getting-started/cache-components), you can combine `generateStaticParams` with `use cache` to enable data caching for both prerendered and runtime params.
+
+See the [generateStaticParams with Route Handlers](/docs/app/api-reference/functions/generate-static-params#with-route-handlers) documentation for examples and details.
+
### URL Query Parameters
The request object passed to the Route Handler is a `NextRequest` instance, which includes [some additional convenience methods](/docs/app/api-reference/functions/next-request#nexturl), such as those for more easily handling query parameters.
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 aecceb326115f..f10c99aaa1662 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
@@ -5,6 +5,12 @@ description: API reference for the generateStaticParams function.
The `generateStaticParams` function can be used in combination with [dynamic route segments](/docs/app/api-reference/file-conventions/dynamic-routes) to [**statically generate**](/docs/app/guides/caching#static-rendering) routes at build time instead of on-demand at request time.
+`generateStaticParams` can be used with:
+
+- [Pages](/docs/app/api-reference/file-conventions/page) (`page.tsx`/`page.js`)
+- [Layouts](/docs/app/api-reference/file-conventions/layout) (`layout.tsx`/`layout.js`)
+- [Route Handlers](/docs/app/api-reference/file-conventions/route) (`route.ts`/`route.js`)
+
```tsx filename="app/blog/[slug]/page.tsx" switcher
// Return a list of `params` to populate the [slug] dynamic segment
export async function generateStaticParams() {
@@ -54,6 +60,14 @@ export default async function Page({ params }) {
> - During revalidation (ISR), `generateStaticParams` will not be called again.
> - `generateStaticParams` replaces the [`getStaticPaths`](/docs/pages/api-reference/functions/get-static-paths) function in the Pages Router.
+### With Cache Components
+
+When using [Cache Components](/docs/app/getting-started/cache-components) with dynamic routes, `generateStaticParams` must return **at least one param**. Empty arrays cause a [build error](/docs/messages/empty-generate-static-params). This allows Cache Components to validate your route doesn't incorrectly access `cookies()`, `headers()`, or `searchParams` at runtime.
+
+> **Good to know**: If you don't know the actual param values at build time, you can return a placeholder param (e.g., `[{ slug: '__placeholder__' }]`) for validation, then handle it in your page with `notFound()`. However, this prevents build time validation from working effectively and may cause runtime errors.
+
+See the [dynamic routes section](/docs/app/api-reference/file-conventions/dynamic-routes#with-cache-components) for detailed walkthroughs.
+
## Parameters
`options.params` (optional)
@@ -291,12 +305,72 @@ export async function generateStaticParams() {
}
```
-> **Good to know:** You must always return an array from `generateStaticParams`, even if it's empty. Otherwise, the route will be dynamically rendered.
+> **Good to know:**
+>
+> - You must always return an array from `generateStaticParams`, even if it's empty. Otherwise, the route will be dynamically rendered.
```jsx filename="app/changelog/[slug]/page.js"
export const dynamic = 'force-static'
```
+### With Route Handlers
+
+You can use `generateStaticParams` with [Route Handlers](/docs/app/api-reference/file-conventions/route) to statically generate API responses at build time:
+
+```ts filename="app/api/posts/[id]/route.ts" switcher
+export async function generateStaticParams() {
+ return [{ id: '1' }, { id: '2' }, { id: '3' }]
+}
+
+export async function GET(
+ request: Request,
+ { params }: RouteContext<'/api/posts/[id]'>
+) {
+ const { id } = await params
+ // This will be statically generated for IDs 1, 2, and 3
+ return Response.json({ id, title: `Post ${id}` })
+}
+```
+
+```js filename="app/api/posts/[id]/route.js" switcher
+export async function generateStaticParams() {
+ return [{ id: '1' }, { id: '2' }, { id: '3' }]
+}
+
+export async function GET(request, { params }) {
+ const { id } = await params
+ // This will be statically generated for IDs 1, 2, and 3
+ return Response.json({ id, title: `Post ${id}` })
+}
+```
+
+#### With Cache Components
+
+When using [Cache Components](/docs/app/getting-started/cache-components), combine with `use cache` for optimal caching:
+
+```ts filename="app/api/posts/[id]/route.ts"
+export async function generateStaticParams() {
+ return [{ id: '1' }, { id: '2' }, { id: '3' }]
+}
+
+async function getPost(id: Promise) {
+ 'use cache'
+ const resolvedId = await id
+ const response = await fetch(`https://api.example.com/posts/${resolvedId}`)
+ return response.json()
+}
+
+export async function GET(
+ request: Request,
+ { params }: RouteContext<'/api/posts/[id]'>
+) {
+ const post = await getPost(params.then((p) => p.id))
+ return Response.json(post)
+}
+```
+
+See the [Route Handlers documentation](/docs/app/api-reference/file-conventions/route#static-generation-with-generatestaticparams) for more details.
+
### Disable rendering for unspecified paths
To prevent unspecified paths from being statically rendered at runtime, add the `export const dynamicParams = false` option in a route segment. When this config option is used, only paths provided by `generateStaticParams` will be served, and unspecified routes will 404 or match (in the case of [catch-all routes](/docs/app/api-reference/file-conventions/dynamic-routes#catch-all-segments)).