---
title: "Next.js encountered `prefetch()` or `navigation()` outside of Suspense"
url: "https://nextjs.org/docs/messages/instant-navigation-stage"
docs_index: /docs/llms.txt
---



<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, a Server Component called [`prefetch()`](/docs/app/api-reference/functions/prefetch) or [`navigation()`](/docs/app/api-reference/functions/navigation) outside of [`<Suspense>`](https://react.dev/reference/react/Suspense). These [navigation stage APIs](/docs/app/glossary#navigation-stages) need a fallback when they exclude a subtree from a prefetch.

During runtime rendering, `prefetch()` excludes subsequent content from the [App Shell](/docs/app/glossary#app-shell) but includes it in `<Link prefetch={true}>` prefetches and navigations. `navigation()` excludes the content from both the App Shell and per-link prefetches, then renders it during navigation. Neither API affects initial loads or [static prerenders](/docs/app/glossary#prerendering), and subsequent content remains cacheable.

For the same insight in metadata or viewport code, see [`prefetch()` or `navigation()` in `generateMetadata()`](/docs/messages/instant-navigation-stage-metadata) or [`prefetch()` or `navigation()` in `generateViewport()`](/docs/messages/instant-navigation-stage-viewport).

## Ways to fix this

<FixCardGrid>
  <FixCard
    group="stream"
    title="Wrap in or move into Suspense"
    href="#wrap-in-or-move-into-suspense"
    snippets={[
      { text: '<Suspense fallback={…}>', highlight: true },
      { text: '  <DataChild />' },
      { text: '</Suspense>', highlight: true },
    ]}
  />
  <FixCard
    group="block"
    title="Allow blocking route"
    href="#allow-blocking-route"
    snippets={[
      { text: '// page.tsx or layout.tsx' },
      { text: 'export const instant = false', highlight: true },
    ]}
  />
</FixCardGrid>

## Wrap in or move into Suspense

Choose this fix when the route has meaningful UI before the deferred content. Place [`<Suspense>`](https://react.dev/reference/react/Suspense) around the smallest region that calls the navigation stage API.

### Patterns

#### Include content in a per-link prefetch

Use `prefetch()` when the region should be absent from the App Shell but available to links that use `prefetch={true}`.

```jsx filename="app/products/page.js"
import { Suspense } from 'react'
import { prefetch } from 'next/cache'

async function Recommendations() {
  await prefetch()
  return <ProductGrid products={await getRecommendations()} />
}

export default function Page() {
  return (
    <main>
      <h1>Products</h1>
      <Suspense fallback={<ProductGridSkeleton />}>
        <Recommendations />
      </Suspense>
    </main>
  )
}
```

Learn more: [`prefetch()`](/docs/app/api-reference/functions/prefetch).

#### Defer content until navigation

Use `navigation()` when the region should be absent from prefetches and rendered only after navigation begins.

```jsx filename="app/dashboard/page.js"
import { Suspense } from 'react'
import { navigation } from 'next/cache'

async function RecentActivity() {
  await navigation()
  return <ActivityList activity={await getRecentActivity()} />
}

export default function Page() {
  return (
    <Dashboard>
      <h1>Overview</h1>
      <Suspense fallback={<ActivitySkeleton />}>
        <RecentActivity />
      </Suspense>
    </Dashboard>
  )
}
```

Learn more: [`navigation()`](/docs/app/api-reference/functions/navigation) and [Streaming](/docs/app/guides/streaming).

### Trade-off

The earlier prefetch contains the fallback instead of the deferred content. A per-link prefetch adds speculative server work for content below `prefetch()`, while content below `navigation()` does not begin rendering until navigation.

### Gotchas

- The boundary must be above the component that awaits the navigation stage API. Adding a boundary inside that component does not catch the suspension.
- Navigation stage APIs cannot run inside a `use cache`, `use cache: private`, or [`unstable_cache`](/docs/app/api-reference/functions/unstable_cache) scope. Put the cache directive on a function called after the stage boundary.
- Static prerenders resolve both functions because the work runs once and is shared. Next.js still requires a boundary so the route remains valid if it later needs a runtime App Shell.

## Allow blocking route

Choose this fix when the route has no meaningful UI before the deferred content. Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` exempts the segment from instant-navigation validation.

### Patterns

#### Opt the page out

Add the export to the page that calls the navigation stage API. Only that route is allowed to block.

```jsx filename="app/dashboard/page.js"
import { navigation } from 'next/cache'

export const instant = false

export default async function Page() {
  await navigation()
  return <Dashboard />
}
```

Learn more: [Ensuring instant navigations](/docs/app/guides/instant-navigation).

#### Opt the layout out

Add the export to a layout when the shared segment has no useful earlier state.

```jsx filename="app/dashboard/layout.js"
export const instant = false

export default function DashboardLayout({ children }) {
  return <DashboardShell>{children}</DashboardShell>
}
```

Learn more: [Route segment `instant` config](/docs/app/api-reference/file-conventions/route-segment-config/instant).

Use either pattern when:

- The segment intentionally has no meaningful UI before the point selected by the navigation stage API.
- You're migrating the route incrementally and need to preserve its current blocking behavior.

Don't use this to dismiss the insight. Choose [Wrap in or move into Suspense](#wrap-in-or-move-into-suspense) when a useful fallback exists.

### Trade-off

Navigations to this route are not instant. The browser waits for the full server render before receiving HTML. Use this only when the route requires that delay.

### Gotchas

- Setting [`instant`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to `false` opts out only the segment that exports it. Descendant segments remain validated by their own config or the global default.
- This export does not disable [prerendering](/docs/app/glossary#prerendering). The route still prerenders if it can. It only disables instant-navigation validation for the route.

## 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`](/docs/app/api-reference/cli/next#next-dev-options), the error overlay identifies the failing component with a file path and line number. The default [`next build`](/docs/app/api-reference/cli/next#next-build-options) 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](/docs/app/api-reference/config/next-config-js/cacheComponents) apps and surfaces this insight.

- **One segment**: add [`export const instant = false`](/docs/app/api-reference/file-conventions/route-segment-config/instant) to the page or layout file. This opts out the segment itself. Child segments are still validated during client navigations.
- **Entire app**: set [`experimental.instantInsights.validationLevel`](/docs/app/api-reference/file-conventions/route-segment-config/instant#configuring-validation-defaults) to `'manual-warning'` in `next.config`. This limits validation to segments that explicitly export `instant`.

See [Ensuring instant navigations](/docs/app/guides/instant-navigation) for the full model.

## Related Insights

See [Instant Navigation validation errors](/docs/messages/instant-navigation-validation) for the complete reference.
