Incremental Static Regeneration - docs update explaining surprising behavior (#66151)
The _requirement_ to export a `generateStaticParams` to get static cache behavior _after_ build time was _really_ surprising behavior for me, and I think others: * https://github.com/vercel/next.js/issues/62195#issuecomment-1952091312 * https://github.com/vercel/next.js/discussions/57961#discussioncomment-8491488 Potentially this is a bug, and not something that should be fixed with documentation? I don't understand next.js caching enough to make that determination, so instead I'm proposing these changes to docs which might be encountered by folks who are surprised by this cache behavior. One point in favor of this being a bug: The CLI reports that a route is `SSG` enabled in the build output, but doesn't actually cache post-build page renders if this export is missing. @awinogrodzki made a demo repo showing this behavior, as described [here](https://github.com/vercel/next.js/discussions/57961#discussioncomment-7468144). --------- Co-authored-by: Delba de Oliveira <delbabrown@gmail.com> Co-authored-by: Delba de Oliveira <32464864+delbaoliveira@users.noreply.github.com> Co-authored-by: samcx <sam@vercel.com>
This commit is contained in:
parent
6c1d700afc
commit
5be04cf8fd
3 changed files with 102 additions and 22 deletions
|
@ -536,9 +536,42 @@ See the [Route Segment Config](/docs/app/api-reference/file-conventions/route-se
|
|||
|
||||
For [dynamic segments](/docs/app/building-your-application/routing/dynamic-routes) (e.g. `app/blog/[slug]/page.js`), paths provided by `generateStaticParams` are cached in the Full Route Cache at build time. At request time, Next.js will also cache paths that weren't known at build time the first time they're visited.
|
||||
|
||||
You can disable caching at request time by using `export const dynamicParams = false` option in a route segment. When this config option is used, only paths provided by `generateStaticParams` will be served, and other routes will 404 or match (in the case of [catch-all routes](/docs/app/building-your-application/routing/dynamic-routes#catch-all-segments)).
|
||||
To statically render all paths at build time, supply the full list of paths to `generateStaticParams`:
|
||||
|
||||
See the [`generateStaticParams` API reference](/docs/app/api-reference/functions/generate-static-params).
|
||||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://.../posts').then((res) => res.json())
|
||||
|
||||
return posts.map((post) => ({
|
||||
slug: post.slug,
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
To statically render a subset of paths at build time, and the rest the first time they're visited at runtime, return a partial list of paths:
|
||||
|
||||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://.../posts').then((res) => res.json())
|
||||
|
||||
// Render the first 10 posts at build time
|
||||
return posts.slice(0, 10).map((post) => ({
|
||||
slug: post.slug,
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
To statically render all paths the first time they're visited, return an empty array (no paths will be rendered at build time):
|
||||
|
||||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||||
export async function generateStaticParams() {
|
||||
return []
|
||||
}
|
||||
```
|
||||
|
||||
> **Good to know:** You must always return an array from `generateStaticParams`, even if it's empty. Otherwise, the route will be dynamically rendered.
|
||||
|
||||
To disable caching at request time, 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 other routes will 404 or match (in the case of [catch-all routes](/docs/app/building-your-application/routing/dynamic-routes#catch-all-segments)).
|
||||
|
||||
### React `cache` function
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ export const dynamicParams = true // true | false,
|
|||
> **Good to know**:
|
||||
>
|
||||
> - This option replaces the `fallback: true | false | blocking` option of `getStaticPaths` in the `pages` directory.
|
||||
> - To statically render all paths the first time they're visited, you'll need to return an empty array in `generateStaticParams`.
|
||||
> - When `dynamicParams = true`, the segment uses [Streaming Server Rendering](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense).
|
||||
> - If the `dynamic = 'error'` and `dynamic = 'force-static'` are used, it'll change the default of `dynamicParams` to `false`.
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ export default function Page({ params }) {
|
|||
> **Good to know**
|
||||
>
|
||||
> - You can use the [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams) segment config option to control what happens when a dynamic segment is visited that was not generated with `generateStaticParams`.
|
||||
> - You must always return [an array from `generateStaticParams`](#all-paths-at-build-time), even if it's empty.
|
||||
> - During `next dev`, `generateStaticParams` will be called when you navigate to a route.
|
||||
> - During `next build`, `generateStaticParams` runs before the corresponding Layouts or Pages are generated.
|
||||
> - During revalidation (ISR), `generateStaticParams` will not be called again.
|
||||
|
@ -168,6 +169,69 @@ export default function Page({ params }) {
|
|||
|
||||
## Examples
|
||||
|
||||
### Static Rendering
|
||||
|
||||
#### All paths at build time
|
||||
|
||||
To statically render all paths at build time, supply the full list of paths to `generateStaticParams`:
|
||||
|
||||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://.../posts').then((res) => res.json())
|
||||
|
||||
return posts.map((post) => ({
|
||||
slug: post.slug,
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
#### Subset of paths at build time
|
||||
|
||||
To statically render a subset of paths at build time, and the rest the first time they're visited at runtime, return a partial list of paths:
|
||||
|
||||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://.../posts').then((res) => res.json())
|
||||
|
||||
// Render the first 10 posts at build time
|
||||
return posts.slice(0, 10).map((post) => ({
|
||||
slug: post.slug,
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
Then, by using the [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams) segment config option, you can control what happens when a dynamic segment is visited that was not generated with `generateStaticParams`.
|
||||
|
||||
```jsx filename="app/blog/[slug]/page.js"
|
||||
// All posts besides the top 10 will be a 404
|
||||
export const dynamicParams = false
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://.../posts').then((res) => res.json())
|
||||
const topPosts = posts.slice(0, 10)
|
||||
|
||||
return topPosts.map((post) => ({
|
||||
slug: post.slug,
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
#### All paths at runtime
|
||||
|
||||
To statically render all paths the first time they're visited, return an empty array (no paths will be rendered at build time):
|
||||
|
||||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||||
export async function generateStaticParams() {
|
||||
return []
|
||||
}
|
||||
```
|
||||
|
||||
> **Good to know:** You must always return an array from `generateStaticParams`, even if it's empty. Otherwise, the route will be dynamically rendered.
|
||||
|
||||
### 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/building-your-application/routing/dynamic-routes#catch-all-segments)).
|
||||
|
||||
### Multiple Dynamic Segments in a Route
|
||||
|
||||
You can generate params for dynamic segments above the current layout or page, but **not below**. For example, given the `app/products/[category]/[product]` route:
|
||||
|
@ -177,7 +241,7 @@ You can generate params for dynamic segments above the current layout or page, b
|
|||
|
||||
There are two approaches to generating params for a route with multiple dynamic segments:
|
||||
|
||||
### Generate params from the bottom up
|
||||
#### Generate params from the bottom up
|
||||
|
||||
Generate multiple dynamic segments from the child route segment.
|
||||
|
||||
|
@ -217,7 +281,7 @@ export default function Page({ params }) {
|
|||
}
|
||||
```
|
||||
|
||||
### Generate params from the top down
|
||||
#### Generate params from the top down
|
||||
|
||||
Generate the parent segments first and use the result to generate the child segments.
|
||||
|
||||
|
@ -301,24 +365,6 @@ export default function Page({ params }) {
|
|||
|
||||
> **Good to know**: `fetch` requests are automatically [memoized](/docs/app/building-your-application/caching#request-memoization) for the same data across all `generate`-prefixed functions, Layouts, Pages, and Server Components. React [`cache` can be used](/docs/app/building-your-application/caching#react-cache-function) if `fetch` is unavailable.
|
||||
|
||||
### Generate only a subset of params
|
||||
|
||||
You can generate a subset of params for a route by returning an array of objects with only the dynamic segments you want to generate. Then, by using the [`dynamicParams`](/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams) segment config option, you can control what happens when a dynamic segment is visited that was not generated with `generateStaticParams`.
|
||||
|
||||
```jsx filename="app/blog/[slug]/page.js"
|
||||
// All posts besides the top 10 will be a 404
|
||||
export const dynamicParams = false
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const posts = await fetch('https://.../posts').then((res) => res.json())
|
||||
const topPosts = posts.slice(0, 10)
|
||||
|
||||
return topPosts.map((post) => ({
|
||||
slug: post.slug,
|
||||
}))
|
||||
}
|
||||
```
|
||||
|
||||
## Version History
|
||||
|
||||
| Version | Changes |
|
||||
|
|
Loading…
Reference in a new issue