---
title: Uncached data was accessed outside of `<Suspense>`
---

## Why This Error Occurred

When the `cacheComponents` feature is enabled, Next.js expects a parent `Suspense` boundary around any component that awaits data that should be accessed on every user request. The purpose of this requirement is so that Next.js can provide a useful fallback while this data is accessed and rendered.

While some data is inherently only available when a user request is being handled, such as request headers, Next.js assumes that by default any asynchronous data is expected to be accessed each time a user request is being handled unless you specifically cache it using `"use cache"`.

This usually happens when a component does one of the following without a parent `<Suspense>` boundary:

- Awaiting `params` in a Page or Layout, or awaiting `searchParams` in a Page
- Calling `cookies()`, `headers()`, or `connection()`
- Fetching data that isn't cached with `"use cache"`

The fix depends on what data you're accessing and how you want your app to behave — see the examples below.

> **Good to know**: In `next dev`, the error overlay usually points at the failing component. If the error only appears during `next build` (for example in CI), run `next build --debug-prerender` for prerender stack traces that name the exact component.

## Possible Ways to Fix It

### Accessing Data

When you access data using `fetch`, a database client, or any other module which does asynchronous IO, Next.js interprets your intent as expecting the data to load on every user request.

If you are expecting this data to be used while fully or partially prerendering a page you must cache is using `"use cache"`.

Before:

```jsx filename="app/page.js"
async function getRecentArticles() {
  return db.query(...)
}

export default async function Page() {
  const articles = await getRecentArticles(token);
  return <ArticleList articles={articles}>
}
```

After:

```jsx filename="app/page.js"
import { cacheTag, cacheLife } from 'next/cache'

async function getRecentArticles() {
  "use cache"
  // This cache can be revalidated by webhook or server action
  // when you call revalidateTag("articles")
  cacheTag("articles")
  // This cache will revalidate after an hour even if no explicit
  // revalidate instruction was received
  cacheLife('hours')
  return db.query(...)
}

export default async function Page() {
  const articles = await getRecentArticles(token);
  return <ArticleList articles={articles}>
}
```

If this data should be accessed on every user request you must provide a fallback UI using `Suspense` from React. Where you put this Suspense boundary in your application should be informed by the kind of fallback UI you want to render. It can be immediately above the component accessing this data or even in your Root Layout.

Before:

```jsx filename="app/page.js"
async function getLatestTransactions() {
  return db.query(...)
}

export default async function Page() {
  const transactions = await getLatestTransactions(token);
  return <TransactionList transactions={transactions}>
}
```

After:

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

async function TransactionList() {
  const transactions = await db.query(...)
  return ...
}

function TransactionSkeleton() {
  return <ul>...</ul>
}

export default async function Page() {
  return (
    <Suspense fallback={<TransactionSkeleton />}>
      <TransactionList/>
    </Suspense>
  )
}
```

### Headers

If you are accessing request headers using `headers()`, `cookies()`, or `draftMode()`. Consider whether you can move the use of these APIs deeper into your existing component tree.

Before:

```jsx filename="app/inbox.js"
export async function Inbox({ token }) {
  const email = await getEmail(token)
  return (
    <ul>
      {email.map((e) => (
        <EmailRow key={e.id} />
      ))}
    </ul>
  )
}
```

```jsx filename="app/page.js"
import { cookies } from 'next/headers'

import { Inbox } from './inbox'

export default async function Page() {
  const token = (await cookies()).get('token')
  return (
    <Suspense fallback="loading your inbox...">
      <Inbox token={token}>
    </Suspense>
  )
}
```

After:

```jsx filename="app/inbox.js"
import { cookies } from 'next/headers'

export async function Inbox() {
  const token = (await cookies()).get('token')
  const email = await getEmail(token)
  return (
    <ul>
      {email.map((e) => (
        <EmailRow key={e.id} />
      ))}
    </ul>
  )
}
```

```jsx filename="app/page.js"
import { Inbox } from './inbox'

export default async function Page() {
  return (
    <Suspense fallback="loading your inbox...">
      <Inbox>
    </Suspense>
  )
}
```

Alternatively you can add a Suspense boundary above the component that is accessing Request headers.

### Params and SearchParams

Layout `params` and Page `params` and `searchParams` props are promises. If you await them directly in your Page or Layout component without a `<Suspense>` boundary, the page can be blocked from prerendering. You can fix this by:

- Passing the promise to a child component wrapped in `<Suspense>` and awaiting it there
- Adding a [`loading.js`](/docs/app/api-reference/file-conventions/loading) file to the route segment
- Using [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) to provide known param values at build time (see [below](#generatestaticparams))

For example, `const { id } = await params` directly in a Page component is a common trigger for this error:

Before:

```jsx filename="app/product/[id]/page.js"
export default async function ProductPage({ params }) {
  const { id } = await params
  const product = await db.product.find(id)
  return (
    <div>
      <Breadcrumbs />
      <h1>{product.name}</h1>
    </div>
  )
}
```

After:

```jsx filename="app/product/[id]/page.js"
import { Suspense } from 'react'

export default function ProductPage({ params }) {
  return (
    <div>
      <Breadcrumbs />
      <Suspense fallback={<p>Loading product...</p>}>
        <ProductDetails params={params} />
      </Suspense>
    </div>
  )
}

async function ProductDetails({ params }) {
  const { id } = await params
  const product = await db.product.find(id)
  return <h1>{product.name}</h1>
}
```

If the entire page depends on `params` and there is no static content to show, adding a [`loading.js`](/docs/app/api-reference/file-conventions/loading) file to the route segment is a simpler alternative.

The same pattern applies to `searchParams`:

Before:

```jsx filename="app/map.js"
export async function Map({ lat, lng }) {
  const mapData = await fetch(`https://...?lat=${lat}&lng=${lng}`)
  return drawMap(mapData)
}
```

```jsx filename="app/page.js"
import { cookies } from 'next/headers'

import { Map } from './map'

export default async function Page({ searchParams }) {
  const { lat, lng } = await searchParams;
  return (
    <Suspense fallback="loading your inbox...">
      <Map lat={lat} lng={lng}>
    </Suspense>
  )
}
```

After:

```jsx filename="app/map.js"
export async function Map({ coords }) {
  const { lat, lng } = await coords
  const mapData = await fetch(`https://...?lat=${lat}&lng=${lng}`)
  return drawMap(mapData)
}
```

```jsx filename="app/page.js"
import { cookies } from 'next/headers'

import { Map } from './map'

export default async function Page({ searchParams }) {
  const coords = searchParams.then(sp => ({ lat: sp.lat, lng: sp.lng }))
  return (
    <Suspense fallback="loading your inbox...">
      <Map coord={coords}>
    </Suspense>
  )
}
```

Alternatively you can add a Suspense boundary above the component that is accessing `params` or `searchParams` so Next.js understands what UI should be used when while waiting for this request data to be accessed.

#### `generateStaticParams`

For Layout and Page `params`, you can use [`generateStaticParams`](/docs/app/api-reference/functions/generate-static-params) to provide sample values for build-time validation, which allows you to await params directly without Suspense.

```jsx filename="app/blog/[slug]/page.js"
export async function generateStaticParams() {
  return [{ slug: 'hello-world' }]
}

export default async function Page({ params }) {
  const { slug } = await params //  Valid with generateStaticParams
  return <div>Blog post: {slug}</div>
}
```

Note that validation is path-dependent. Runtime parameters may trigger conditional branches accessing runtime APIs without Suspense, or dynamic content without Suspense or `use cache`, resulting in errors. See [Dynamic Routes with Cache Components](/docs/app/api-reference/file-conventions/dynamic-routes#with-cache-components).

### Short-lived Caches

`"use cache"` allows you to describe a [`cacheLife()`](/docs/app/api-reference/functions/cacheLife) that might be too short to be practical to prerender. The utility of doing this is that it can still describe a non-zero caching time for the client router cache to reuse the cache entry in the browser and it can also be useful for protecting upstream APIs while experiencing high request traffic.

If you expected the `"use cache"` entry to be prerenderable try describing a slightly longer `cacheLife()`.

Before:

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

async function getDashboard() {
  "use cache"
  // This cache will revalidate after 1 second. It is so short
  // Next.js won't prerender it on the server but the client router
  // can reuse the result for up to 30 seconds unless the user manually refreshes
  cacheLife('seconds')
  return db.query(...)
}

export default async function Page() {
  const data = await getDashboard(token);
  return <Dashboard data={data}>
}
```

After:

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

async function getDashboard() {
  "use cache"
  // This cache will revalidate after 1 minute. It's long enough that
  // Next.js will still produce a fully or partially prerendered page
  cacheLife('minutes')
  return db.query(...)
}

export default async function Page() {
  const data = await getDashboard(token);
  return <Dashboard data={data}>
}
```

Alternatively you can add a Suspense boundary above the component that is accessing this short-lived cached data so Next.js understands what UI should be used while accessing this data on a user request.

## Useful Links

- [`Suspense` React API](https://react.dev/reference/react/Suspense)
- [`headers` function](/docs/app/api-reference/functions/headers)
- [`cookies` function](/docs/app/api-reference/functions/cookies)
- [`draftMode` function](/docs/app/api-reference/functions/draft-mode)
- [`connection` function](/docs/app/api-reference/functions/connection)
- [`cacheLife` function](/docs/app/api-reference/functions/cacheLife)
- [`cacheTag` function](/docs/app/api-reference/functions/cacheTag)
