Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/01-app/02-guides/migrating-to-cache-components.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,8 @@ Paths you don't return are still served. Next.js prerenders a static shell for t

Params not returned by `generateStaticParams` are rendered on request. If you used `dynamicParams: false` to reject them, call [`notFound()`](/docs/app/api-reference/functions/not-found) in the page when the param doesn't resolve to real data.

If this check runs after the Cache Components [static shell](/docs/app/glossary#static-shell) starts streaming, the response keeps its `200` status and Next.js adds a `noindex` meta tag. If you require an HTTP `404`, validate the path before the response streams, such as in [`proxy`](/docs/app/api-reference/file-conventions/proxy). See [Status Codes](/docs/app/api-reference/file-conventions/loading#status-codes).

### Await `params` inside `<Suspense>`

To produce the static shell, pass the `params` promise into a [`<Suspense>`](/docs/app/api-reference/file-conventions/loading) boundary instead of awaiting it at the top of the component, so unknown params can still prerender.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { notFound } from 'next/navigation'

const prerenderedSlugs = ['known']
const runtimeSlugs = ['runtime']

export const instant = false

export function generateStaticParams() {
return prerenderedSlugs.map((slug) => ({ slug: [slug] }))
}

export default async function Page({
params,
}: {
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params
const pathname = slug?.join('/') ?? ''

if (
!prerenderedSlugs.includes(pathname) &&
!runtimeSlugs.includes(pathname)
) {
notFound()
}

return <p id="blocking-slug">{pathname}</p>
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,25 @@ describe('partial-fallback-shell-upgrade - partialPrefetching disabled', () => {
'generic shell should remain shared without partialPrefetching'
)
})

it('keeps blocking not-found responses stable after the first request', async () => {
for (let attempt = 0; attempt < 2; attempt++) {
const response = await next.fetch('/blocking/missing')
const html = await response.text()

expect(response.status).toBe(404)
expect(html).toContain('<meta name="robots" content="noindex"')
}
})

it('serves valid ungenerated params before and after the first request', async () => {
for (let attempt = 0; attempt < 2; attempt++) {
const response = await next.fetch('/blocking/runtime')

expect(response.status).toBe(200)
expect(await response.text()).toContain(
'<p id="blocking-slug">runtime</p>'
)
}
})
})
Loading