instant-navigation-stage-metadata.mdx


title: Next.js encountered prefetch() or navigation() in generateMetadata() kind: insight

<div style={{ padding: '1.25rem 1.5rem', border: '1px solid var(--ds-gray-400)', borderRadius: '12px', background: 'var(--ds-background-200)', margin: '1.5rem 0 2rem', fontSize: '0.95rem', lineHeight: '1.6', }} > This insight is part of the [Instant Navigations](https://nextjs.org/blog/next-16-3-instant-navigations) feature introduced in Next.js 16.3. If you're new to it, start with the [Ensuring instant navigations](/docs/app/guides/instant-navigation) guide for an overview of what instant navigations are and how Next.js validates them, then come back here for the specific fix. </div>

During prerendering or a client-side navigation, generateMetadata() called prefetch() or navigation(). These navigation stage APIs delay the metadata work until a per-link prefetch or navigation, depending on which API you use.

Metadata can already stream, so delaying it may be unintentional. Remove the API call if you do not need the delay. Otherwise, confirm that the metadata delay is intentional.

For the same insight in the page body or viewport code, see prefetch() or navigation() outside of Suspense or prefetch() or navigation() in generateViewport().

Ways to fix this

<FixCardGrid> <FixCard group="remove" title="Remove the API call" href="#remove-the-api-call" snippets={[ { text: 'async function generateMetadata() {' }, { text: '- await navigation()', highlight: true }, { text: '}' }, ]} /> <FixCard group="mark" title="Confirm the metadata delay" href="#confirm-the-metadata-delay" snippets={[ { text: '// page.tsx or layout.tsx' }, { text: 'await navigation()', highlight: true }, ]} /> </FixCardGrid>

Remove the API call

Choose this fix when you do not need to control when the metadata is included. Remove prefetch() or navigation() from generateMetadata(). Keep generateMetadata() and its existing caching.

Patterns

Remove the API call

Remove the call to prefetch() or navigation().

import { getMetadata } from './data'

export async function generateMetadata() {
  return getMetadata()
}

export default function Page() {
  return <main>Dashboard</main>
}

Learn more: generateMetadata().

Trade-off

Removing the API can increase the metadata work performed during prerendering and prefetching.

Gotchas

  • You do not need to replace generateMetadata() with a static metadata export. Use a static export only when the values are fixed for the route segment.

Confirm the metadata delay

Choose this fix when the page can render earlier but the metadata should remain at the selected navigation stage. Create a component that calls the same API as generateMetadata() and returns null. Render it inside <Suspense>.

This insight appears because metadata is the only part of the route delayed to that stage. Calling the same API from the page confirms that the timing is intentional.

Patterns

Add a navigation stage marker

Create the marker and render it inside <Suspense>. The existing page content remains available at its current stages.

This example uses navigation(). If generateMetadata() calls prefetch(), call prefetch() in the marker instead.

import { Suspense } from 'react'
import { navigation } from 'next/cache'
import { getMetadata } from './data'

export async function generateMetadata() {
  await navigation()
  return getMetadata()
}

async function NavigationStageMarker() {
  await navigation()
  return null
}

export default function Page() {
  return (
    <>
      <main>Dashboard</main>
      <Suspense>
        <NavigationStageMarker />
      </Suspense>
    </>
  )
}

Learn more: prefetch() and navigation().

Trade-off

The metadata remains cacheable, but is unavailable until the selected navigation stage.

Gotchas

  • The marker must call the same navigation stage API as generateMetadata().
  • The marker must render inside <Suspense>. Without the boundary, the page surfaces navigation stage API outside of Suspense.
  • If the page already calls the same navigation stage API inside a <Suspense> boundary, you won't see this insight. The page already includes work at the same stage.

Verifying the fix

After applying a fix, navigate to the route and confirm the insight no longer appears. The page should paint meaningful UI immediately. Keep <Suspense> fallbacks scoped to the regions that stream in. A boundary around the whole page can pass validation with an empty shell, which defeats instant navigation.

In next dev, the error overlay identifies the failing component with a file path and line number. The default next build output is shorter. Run next build --debug-prerender for full user-frame stack traces or next build --debug-build-paths /dashboard /settings to inspect specific routes.

Don't want this validation?

Instant-navigation validation runs by default in Cache Components apps and surfaces this insight.

See Ensuring instant navigations for the full model.

See Instant Navigation validation errors for the complete reference.

Rendered from the public repository vercel/next.js on GitHub; content belongs to its authors. Cached for up to an hour.

errors/instant-navigation-stage-metadata.mdx · vercel/next.js | Docs MD