Skip to content
Closed
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
12 changes: 8 additions & 4 deletions errors/next-prerender-dynamic-metadata.mdx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
---
title: Cannot access Request information or uncached data in `generateMetadata()` in an otherwise entirely static route
title: Cannot access Runtime data or uncached data in `generateMetadata()` or file-based Metadata in an otherwise entirely static route
---

## Why This Error Occurred

When `cacheComponents` is enabled, Next.js requires that `generateMetadata()` not depend on uncached data or Request data unless some other part of the page also has similar requirements. The reason for this is that while you normally control your intention for what is allowed to be dynamic by adding or removing Suspense boundaries in your Layout and Page components you are not in control of rendering metadata itself.
When `cacheComponents` is enabled, Next.js requires that Document Metadata not depend on uncached data or Runtime data (`cookies()`, `headers()`, `params`, `searchParams`) unless some other part of the page also has similar requirements.

The heuristic Next.js uses to understand your intent with `generateMetadata()` is to look at the data requirements of the rest of the route. If other components depend on Request data or uncached data, then we allow `generateMetadata()` to have similar data requirements. If the rest of your page has no dependence on this type of data, we require that `generateMetadata()` also not have this type of data dependence.
Next.js determines if a page is entirely static or partially static by looking at whether any part of the page cannot be prerendered.

Typically you control the which parts of a page can be prerendered by adding `"use cache"` to Components or data functions and by avoiding Runtime data like `cookies()` or `searchParams`. However, Metadata can be defined in functions (`generateMetadata()`) defined far from your Page content. Additionally Metadata can implicitly depend on Runtime data when using file-based Metadata such as an icon inside a route with a dynamic param. It would be easy for Metadata to accidentally make an otherwise entirely static page have a dynamic component.

To prevent anwanted partially dynamic pages, Next.js expects pages that are otherwise entirely prerenderable to also have prerenderable Metadata.

## Possible Ways to Fix It

Expand Down Expand Up @@ -141,7 +145,7 @@ export default function Page() {
}
```

Note: The reason to structure this `DynamicMarker` as a self-contained Suspense boundary is to avoid blocking the actual content of the page from being prerendered. When Partial Prerendering is enabled alongside `cacheComponents`, the static shell will still contain all of the prerenderable content, and only the metadata will stream in dynamically.
Note: The reason to structure this `DynamicMarker` as a self-contained Suspense boundary is to avoid blocking the actual content of the page from being prerendered.

## Useful Links

Expand Down
92 changes: 48 additions & 44 deletions errors/next-prerender-dynamic-viewport.mdx
Original file line number Diff line number Diff line change
@@ -1,16 +1,61 @@
---
title: Cannot access Request information or uncached data in `generateViewport()`
title: Cannot access Runtime data or uncached data in `generateViewport()`
---

## Why This Error Occurred

When `cacheComponents` is enabled, Next.js requires that `generateViewport()` not depend on uncached data or Request data unless you explicitly opt into having a fully dynamic page. If you encountered this error, it means that `generateViewport` depends on one of these types of data and you have not specifically indicated that the affected route should be entirely dynamic.
When `cacheComponents` is enabled, Next.js requires that `generateViewport()` not depend on uncached data or Runtime data (`cookies()`, `headers()`, `params`, `searchParams`) unless you explicitly opt into having a fully dynamic page. If you encountered this error, it means that `generateViewport` depends on one of these types of data and you have not specifically indicated that blocking navigations are acceptable.

## Possible Ways to Fix It

To fix this issue, you must first determine your goal for the affected route.

Normally, the way you indicate to Next.js that you want to allow reading Request data or uncached external data is by performing this data access inside a component with an ancestor Suspense boundary. With Viewport, however, you aren't directly in control of wrapping the location where this metadata will be rendered, and even if you could wrap it in a Suspense boundary, it would not be correct to render it with a fallback. This is because this metadata is critical to properly loading resources such as images and must be part of the initial App Shell (the initial HTML containing the document head as well as the first paintable UI).
Normally, Next.js ensures every page can produce an initial UI that allows the page to start loading even before uncached data and Runtime data is available. This is accomplished by defining prerenderable UI with Suspense. However viewport metadata is not able to be deferred until after the page loads because it affects initial page load UI.

Ideally, you update `generateViewport` so it does not depend on any uncached data or Runtime data. This allows navigations to appear instant.

However if this is not possibl you can instruct Next.js to allow all navigations to be potentially blocking by wrapping your document `<body>` in a Suspense boundary.

### Caching External Data

When external data is cached, Next.js can prerender with it, which ensures that the App Shell always has the complete viewport metadata available. Consider using `"use cache"` to mark the function producing the external data as cacheable.

Before:

```jsx filename="app/.../layout.tsx"
import { db } from './db'

export async function generateViewport() {
const { width, initialScale } = await db.query('viewport-size')
return {
width,
initialScale,
}
}

export default async function Layout({ children }) {
return ...
}
```

After:

```jsx filename="app/.../layout.tsx"
import { db } from './db'

export async function generateViewport() {
"use cache"
const { width, initialScale } = await db.query('viewport-size')
return {
width,
initialScale,
}
}

export default async function Layout({ children }) {
return ...
}
```

### If you must access Request Data or your external data is uncacheable

Expand Down Expand Up @@ -61,47 +106,6 @@ export default function RootLayout({ children }) {
}
```

### Caching External Data

When external data is cached, Next.js can prerender with it, which ensures that the App Shell always has the complete viewport metadata available. Consider using `"use cache"` to mark the function producing the external data as cacheable.

Before:

```jsx filename="app/.../layout.tsx"
import { db } from './db'

export async function generateViewport() {
const { width, initialScale } = await db.query('viewport-size')
return {
width,
initialScale,
}
}

export default async function Layout({ children }) {
return ...
}
```

After:

```jsx filename="app/.../layout.tsx"
import { db } from './db'

export async function generateViewport() {
"use cache"
const { width, initialScale } = await db.query('viewport-size')
return {
width,
initialScale,
}
}

export default async function Layout({ children }) {
return ...
}
```

## Useful Links

- [`generateViewport()`](/docs/app/api-reference/functions/generate-viewport)
Expand Down
145 changes: 145 additions & 0 deletions errors/next-prerender-runtime-crypto.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
title: Cannot access `crypto.getRandomValue()`, `crypto.randomUUID()`, or another web or node crypto API that generates random values synchronously before other uncached data or `connection()` in a Server Component
---

## Why This Error Occurred

An API that produces a random value synchronously from the Web Crypto API or from Node's `crypto` package was used in a Server Component before accessing other uncached data through APIs like `fetch()` and native database drivers, or the `connection()` API. While typically random crypto values can be guarded behind Runtime data like `cookies()`, `headers()`, `params`, and `searchParams`, this particular route is configured for Runtime Prefetching which makes these APIs available as part of the prefetch request. Accessing random values synchronously without preceding it with uncached data or `await connection()` interferes with the framework's ability to produce a correct prefetch result.

## Possible Ways to Fix It

If the random crypto value is appropriate to be prefetched consider moving it into a Cache Component or Cache Function with the `"use cache"` directive.

If the random crypto value is intended to be generated on every user navigation consider whether an async API exists that achieves the same result. If not consider whether you can move the random crypto value generation later, behind other existing uncached data or Request data access. If there is no way to do this you can always precede the random crypto value generation with Request data access by using `await connection()`.

### Cache the token value

If you are generating a token to talk to a database that itself should be cached move the token generation inside the `"use cache"`.

Before:

```jsx filename="app/page.js"
export const unstable_prefetch = {
mode: 'runtime',
samples: [...],
}

async function getCachedData(token: string, userId: string) {
"use cache"
return db.query(token, userId, ...)
}

export default async function Page({ params }) {
const { userId } = await params
const token = crypto.randomUUID()
const data = await getCachedData(token, userId);
return ...
}
```

After:

```jsx filename="app/page.js"
export const unstable_prefetch = {
mode: 'runtime',
samples: [...],
}

async function getCachedData(userId: string) {
"use cache"
const token = crypto.randomUUID()
return db.query(token, userId, ...)
}

export default async function Page({ params }) {
const { userId } = await params
const data = await getCachedData(userId);
return ...
}
```

### Use an async API at request-time

If you require this random value to be unique per Request and an async version of the API exists switch to it instead. Also ensure that there is a parent Suspense boundary that defines a fallback UI Next.js can use while rendering this component on each Request.

Before:

```jsx filename="app/page.js"
export const unstable_prefetch = {
mode: 'runtime',
samples: [...],
}

import { generateKeySync } from 'node:crypto'

export default async function Page({ params }) {
const { dataId } = await params
const data = await fetchData(dataId)
const key = generateKeySync('hmac', { ... })
const digestedData = await digestDataWithKey(data, key);
return ...
}
```

After:

```jsx filename="app/page.js"
export const unstable_prefetch = {
mode: 'runtime',
samples: [...],
}

import { generateKey } from 'node:crypto'

export default async function Page({ params }) {
const { dataId } = await params
const data = await fetchData(dataId)
const key = await new Promise(resolve => generateKey('hmac', { ... }, key => resolve(key)))
const digestedData = await digestDataWithKey(data, key);
return ...
}
```

### Use `await connection()` at request-time

If you require this random value to be unique per Request and an async version of the API does not exist, call `await connection()`. Also ensure that there is a parent Suspense boundary that defines a fallback UI Next.js can use while rendering this component on each Request.

Before:

```jsx filename="app/page.js"
export const unstable_prefetch = {
mode: 'runtime',
samples: [...],
}

export default async function Page({ params }) {
const { sessionId } = await params
const uuid = crypto.randomUUID()
return <RequestId sessionId={sessionId} id={uuid} />
}
```

After:

```jsx filename="app/page.js"
export const unstable_prefetch = {
mode: 'runtime',
samples: [...],
}

import { connection } from 'next/server'

export default async function Page({ params }) {
await connection()
const { sessionId } = await params
const uuid = crypto.randomUUID()
return <RequestId sessionId={sessionId} id={uuid} />
}
```

## Useful Links

- [`connection` function](/docs/app/api-reference/functions/connection)
- [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API)
- [Node Crypto API](https://nodejs.org/docs/latest/api/crypto.html)
- [`Suspense` React API](https://react.dev/reference/react/Suspense)
Loading
Loading