diff --git a/errors/next-prerender-dynamic-metadata.mdx b/errors/next-prerender-dynamic-metadata.mdx index 17f835df14fbc..40c4ba0a0aa6e 100644 --- a/errors/next-prerender-dynamic-metadata.mdx +++ b/errors/next-prerender-dynamic-metadata.mdx @@ -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 @@ -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 diff --git a/errors/next-prerender-dynamic-viewport.mdx b/errors/next-prerender-dynamic-viewport.mdx index 59993c95a0f67..4603cf2d0791c 100644 --- a/errors/next-prerender-dynamic-viewport.mdx +++ b/errors/next-prerender-dynamic-viewport.mdx @@ -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 `
` 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 @@ -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) diff --git a/errors/next-prerender-runtime-crypto.mdx b/errors/next-prerender-runtime-crypto.mdx new file mode 100644 index 0000000000000..8366bcbb62070 --- /dev/null +++ b/errors/next-prerender-runtime-crypto.mdx @@ -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- This delays the entire page from rendering, resulting in a slow user - experience. Next.js uses this error to ensure your app loads instantly - on every navigation. -
-
- Wrap the component in a {'
-
- Move the asynchronous await into a Cache Component (
- "use cache")
-
- . This allows Next.js to statically prerender the component as part of
- the HTML document, so it's instantly visible to the user.
-
- Note that request-specific information — such as params, cookies,
- and headers — is not available during static prerendering, so must
- be wrapped in {'
- Learn more:{' '} - - https://nextjs.org/docs/messages/blocking-route - -
-generateMetadata() in an otherwise prerenderable page
+
+ When Document metadata is the only part of a page that cannot be
+ prerendered Next.js expects you to either make it prerenderable or
+ make some other part of the page non-prerenderable to avoid
+ unintentional partially dynamic pages. Uncached data such as{' '}
+ fetch(...), cached data with a low expire time, or{' '}
+ connection() are all examples of data that only resolve
+ on navigation.
+
+
+ Move the asynchronous await into a Cache Component (
+ "use cache")
+
+ . This allows Next.js to statically prerender{' '}
+ generateMetadata() as part of the HTML document, so it's
+ instantly visible to the user.
+
+
+ add connection() inside a {'
+ {' '}
+ somewhere in a Page or Layout. This tells Next.js that the page is
+ intended to have some non-prerenderable parts.
+
+ Learn more:{' '} + + https://nextjs.org/docs/messages/next-prerender-dynamic-metadata + +
+generateMetadata() or
+ file-based metadata
+ + When Document metadata is the only part of a page that cannot be + prerendered Next.js expects you to either make it prerenderable or + make some other part of the page non-prerenderable to avoid + unintentional partially dynamic pages. +
+
+
+ Remove the Runtime data access from generateMetadata()
+
+ . This allows Next.js to statically prerender{' '}
+ generateMetadata() as part of the HTML document, so it's
+ instantly visible to the user.
+
+
+ add connection() inside a {'
+ {' '}
+ somewhere in a Page or Layout. This tells Next.js that the page is
+ intended to have some non-prerenderable parts.
+
+ Note that if you are using file-based metadata, such as icons, inside + a route with dynamic params then the only recourse is to make some + other part of the page non-prerenderable. +
++ Learn more:{' '} + + https://nextjs.org/docs/messages/next-prerender-dynamic-metadata + +
+generateViewport()
+
+ Viewport metadata needs to be available on page load so accessing
+ data that waits for a user navigation while producing it prevents
+ Next.js from prerendering an initial UI. Uncached data such as{' '}
+ fetch(...), cached data with a low expire time, or{' '}
+ connection() are all examples of data that only resolve
+ on navigation.
+
+
+ Move the asynchronous await into a Cache Component (
+ "use cache")
+
+ . This allows Next.js to statically prerender{' '}
+ generateViewport() as part of the HTML document, so
+ it's instantly visible to the user.
+
+
+ Put a {' around your document{' '}
+ {''}.
+
+ This indicate to Next.js that you are opting into allowing blocking
+ navigations for any page.
+
+ Learn more:{' '} + + https://nextjs.org/docs/messages/next-prerender-dynamic-viewport + +
+generateViewport()
+
+ Viewport metadata needs to be available on page load so accessing
+ data that comes from a user Request while producing it prevents
+ Next.js from prerendering an initial UI.
+ cookies(), headers(), and{' '}
+ searchParams, are examples of Runtime data that can
+ only come from a user request.
+
+ Remove the Runtime data requirement from{' '}
+ generateViewport. This allows Next.js to statically
+ prerender generateViewport() as part of the HTML
+ document, so it's instantly visible to the user.
+
+
+ Put a {' around your document{' '}
+ {''}.
+
+ This indicate to Next.js that you are opting into allowing blocking
+ navigations for any page.
+
+ params are usually considered Runtime data but if all
+ params are provided a value using generateStaticParams{' '}
+ they can be statically prerendered.
+
+ Learn more:{' '} + + https://nextjs.org/docs/messages/next-prerender-dynamic-viewport + +
+generateMetadata() in an otherwise prerenderable page
+
+ When Document metadata is the only part of a page that cannot be
+ prerendered Next.js expects you to either make it prerenderable or
+ make some other part of the page non-prerenderable to avoid
+ unintentional partially dynamic pages. Uncached data such as{' '}
+ fetch(...), cached data with a low expire time, or{' '}
+ connection() are all examples of data that only resolve
+ on navigation.
+
+
+ Move the asynchronous await into a Cache Component (
+ "use cache")
+
+ . This allows Next.js to statically prerender{' '}
+ generateMetadata() as part of the HTML document, so
+ it's instantly visible to the user.
+
+
+ add connection() inside a {'
+ {' '}
+ somewhere in a Page or Layout. This tells Next.js that the page is
+ intended to have some non-prerenderable parts.
+
+ Learn more:{' '} + + https://nextjs.org/docs/messages/next-prerender-dynamic-metadata + +
+generateMetadata() or
+ file-based metadata
+ + When Document metadata is the only part of a page that cannot be + prerendered Next.js expects you to either make it prerenderable or + make some other part of the page non-prerenderable to avoid + unintentional partially dynamic pages. +
+
+
+ Remove the Runtime data access from{' '}
+ generateMetadata()
+
+ . This allows Next.js to statically prerender{' '}
+ generateMetadata() as part of the HTML document, so
+ it's instantly visible to the user.
+
+
+ add connection() inside a {'
+ {' '}
+ somewhere in a Page or Layout. This tells Next.js that the page is
+ intended to have some non-prerenderable parts.
+
+ Note that if you are using file-based metadata, such as icons, + inside a route with dynamic params then the only recourse is to make + some other part of the page non-prerenderable. +
++ Learn more:{' '} + + https://nextjs.org/docs/messages/next-prerender-dynamic-metadata + +
+
+ This delays the entire page from rendering, resulting in a slow user
+ experience. Next.js uses this error to ensure your app loads instantly
+ on every navigation.
+ cookies(), headers(), and{' '}
+ searchParams, are examples of Runtime data that can only
+ come from a user request.
+
+ Provide a fallback UI using {'
+
+ Move the Runtime data access into a deeper component wrapped in{' '}
+ {'
+ In either case this allows Next.js to stream its contents to the user + when they request the page, while still providing an initial UI that + is prerendered and prefetchable for instant navigations. +
++ Learn more:{' '} + + https://nextjs.org/docs/messages/blocking-route + +
+
+ This delays the entire page from rendering, resulting in a slow user
+ experience. Next.js uses this error to ensure your app loads instantly
+ on every navigation. Uncached data such as fetch(...),
+ cached data with a low expire time, or connection() are
+ all examples of data that only resolve on navigation.
+
+ Provide a fallback UI using {'
+
+ Move the asynchronous await into a Cache Component (
+ "use cache")
+
+ . This allows Next.js to statically prerender the component as part of
+ the HTML document, so it's instantly visible to the user.
+
+ Learn more:{' '} + + https://nextjs.org/docs/messages/blocking-route + +
++ {errorDetails.notes} +
+ > + ) : null} + {errorDetails.warning ? ( +
+
- {notes} -
- > - ) : null} - {hydrationWarning ? ( -
-