Skip to content
Merged
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
15 changes: 14 additions & 1 deletion docs/01-app/03-api-reference/04-functions/unstable_cache.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,22 @@ export default async function Component({ userID }) {

> **Good to know**:
>
> - Accessing uncached data sources such as `headers` or `cookies` inside a cache scope is not supported. If you need this data inside a cached function use `headers` outside of the cached function and pass the required uncached data in as an argument.
> - Accessing request data such as `headers` or `cookies` inside a cache scope is not supported. Read the data outside the cached function and pass the values you need as arguments.
> - This API uses Next.js' built-in cache to persist the result across requests and deployments. See [Caching and Revalidating](/docs/app/getting-started/caching).

## Unsupported operations

The function passed to `unstable_cache` cannot depend on the incoming request. Read values from `cookies()` or `headers()` before calling the cached function, then pass those values as arguments so they become part of the cache key.

The cached function also cannot:

- Call `connection()`, which requires an incoming request
- Enable or disable draft mode
- Call `revalidateTag()`, `updateTag()`, or `revalidatePath()` during cache execution
- Call a function that uses the `use cache: private` directive

Perform request-dependent work before calling the cached function. Perform mutations and revalidation afterward from a Server Action or Route Handler.

## Parameters

```jsx
Expand Down
44 changes: 44 additions & 0 deletions errors/cache-life-outside-use-cache.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: Cannot call `cacheLife()` outside `use cache`
---

## Why This Error Occurred

The `cacheLife()` function configures the `stale`, `revalidate`, and `expire` times for a cache entry. Call it from a function or component that uses the `use cache` or `use cache: private` directive. When you call `cacheLife()` outside a cached function, there is no cache entry to configure.

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

export default async function Page() {
cacheLife('hours') // No cache entry to configure
return <p>...</p>
}
```

## Possible Ways to Fix It

Move `cacheLife()` inside the cached function whose lifetime you want to set.

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

async function getProducts() {
'use cache'
cacheLife('hours') // Configures this cache entry
return db.products.findMany()
}

export default async function Page() {
const products = await getProducts()
return <p>...</p>
}
```

If you don't intend to cache the function or component, remove the `cacheLife()` call.

## Useful Links

- [`cacheLife()` function](/docs/app/api-reference/functions/cacheLife)
- [`cacheTag()` function](/docs/app/api-reference/functions/cacheTag)
- [`use cache` directive](/docs/app/api-reference/directives/use-cache)
- [`use cache: private` directive](/docs/app/api-reference/directives/use-cache-private)
45 changes: 45 additions & 0 deletions errors/cache-tag-outside-use-cache.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: Cannot call `cacheTag()` outside `use cache`
---

## Why This Error Occurred

The `cacheTag()` function attaches one or more tags to a cache entry so you can later revalidate it with `revalidateTag()`. Call it from a function or component that uses the `use cache` or `use cache: private` directive. When you call `cacheTag()` outside a cached function, there is no cache entry to tag.

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

export default async function Page() {
cacheTag('products') // No cache entry to tag
return <p>...</p>
}
```

## Possible Ways to Fix It

Move `cacheTag()` inside the cached function whose entry you want to tag.

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

async function getProducts() {
'use cache'
cacheTag('products') // Tags this cache entry
return db.products.findMany()
}

export default async function Page() {
const products = await getProducts()
return <p>...</p>
}
```

If you don't intend to cache the function or component, remove the `cacheTag()` call. To revalidate the tag later, call [`revalidateTag()`](/docs/app/api-reference/functions/revalidateTag) from a Server Action or Route Handler.

## Useful Links

- [`cacheTag()` function](/docs/app/api-reference/functions/cacheTag)
- [`cacheLife()` function](/docs/app/api-reference/functions/cacheLife)
- [`use cache` directive](/docs/app/api-reference/directives/use-cache)
- [`use cache: private` directive](/docs/app/api-reference/directives/use-cache-private)
- [`revalidateTag()` function](/docs/app/api-reference/functions/revalidateTag)
34 changes: 20 additions & 14 deletions errors/next-request-in-use-cache.mdx
Original file line number Diff line number Diff line change
@@ -1,51 +1,57 @@
---
title: Cannot access `cookies()` or `headers()` in `"use cache"`
title: Cannot access request data in `use cache`
---

## Why This Error Occurred

A function is trying to read from the current incoming request inside the scope of a function annotated with `"use cache"`. This is not supported because it would make the cache invalidated by every request which is probably not what you intended.
A cached function tried to access the incoming request. APIs such as `cookies()`, `headers()`, `searchParams`, and `connection()` depend on the request and cannot be called inside a function that uses the `use cache` directive. This error can also occur when you pass unresolved request data into a cached function during prerendering. You can read draft mode inside a cached function, but enable or disable it outside.

A cached function can also stall if it awaits a promise stored outside the function, such as one kept in a module-scoped `Map` for deduplication.

## Possible Ways to Fix It

Instead of calling this inside the `"use cache"` function, move it outside the function and pass the value in as an argument. The specific value will now be part of the cache key through its arguments.
Read and resolve request data outside the cached function, then pass only the values you need as arguments. Call `connection()` and enable or disable draft mode outside the cached function as well.

If a module-scoped cache or deduplication layer stores promises, remove it. The `use cache` directive already deduplicates calls with the same arguments within a render pass and across requests for as long as the cache entry lasts.

Before:

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

async function getExampleData() {
"use cache"
async function getGreeting() {
'use cache'
const isLoggedIn = (await cookies()).has('token')
...
return isLoggedIn ? 'Welcome back' : 'Welcome'
}

export default async function Page() {
const data = await getExampleData()
return ...
const greeting = await getGreeting()
return <p>{greeting}</p>
}
```

After:

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

async function getExampleData(isLoggedIn) {
"use cache"
...
async function getGreeting(isLoggedIn) {
'use cache'
return isLoggedIn ? 'Welcome back' : 'Welcome'
}

export default async function Page() {
const isLoggedIn = (await cookies()).has('token')
const data = await getExampleData(isLoggedIn)
return ...
const greeting = await getGreeting(isLoggedIn)
return <p>{greeting}</p>
}
```

## Useful Links

- [`headers()` function](/docs/app/api-reference/functions/headers)
- [`cookies()` function](/docs/app/api-reference/functions/cookies)
- [`connection()` function](/docs/app/api-reference/functions/connection)
- [`draftMode()` function](/docs/app/api-reference/functions/draft-mode)
- [`use cache` directive](/docs/app/api-reference/directives/use-cache)
41 changes: 41 additions & 0 deletions errors/revalidate-in-use-cache.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: Cannot revalidate during render, inside a cached function, or in `generateStaticParams`
---

## Why This Error Occurred

The `revalidateTag()` and `revalidatePath()` functions invalidate cached data. To keep cache state consistent, call them from a Server Action or Route Handler. Next.js throws this error when you call either function during rendering, from a cached function, or from `generateStaticParams`.

```jsx filename="app/products/page.js" highlight={5}
import { revalidateTag } from 'next/cache'

export default async function Page() {
const products = await db.products.findMany()
revalidateTag('products', 'max') // Cannot revalidate during render

return <p>{products.length} products</p>
}
```

## Possible Ways to Fix It

Call `revalidateTag()` or `revalidatePath()` from the Server Action or Route Handler itself. Do not call either function during rendering or from a function that uses the `use cache` directive or `unstable_cache()`.

```jsx filename="app/actions.js" highlight={6}
'use server'
import { revalidateTag } from 'next/cache'

export async function saveProduct(data) {
await db.products.create(data)
revalidateTag('products', 'max') // Runs outside any cached function
}
```

To tag the cache entry that this revalidates, call [`cacheTag()`](/docs/app/api-reference/functions/cacheTag) inside the function that uses the `use cache` directive to read the data.

## Useful Links

- [`revalidateTag()` function](/docs/app/api-reference/functions/revalidateTag)
- [`revalidatePath()` function](/docs/app/api-reference/functions/revalidatePath)
- [Revalidating data](/docs/app/getting-started/revalidating)
- [`use cache` directive](/docs/app/api-reference/directives/use-cache)
46 changes: 46 additions & 0 deletions errors/use-cache-private-composition.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: 'Invalid `use cache: private` composition'
---

## Why This Error Occurred

The `use cache: private` directive lets a cached function access request-specific data. Next.js stores its results only in the browser's memory. Private cached functions cannot run inside shared caches or without an active request. This error occurs when a private cached function is:

- **Nested inside a public `use cache` directive.** A shared cached function can reuse its result across users, so it cannot contain a private cached function. Nest a private cached function only inside another private cached function.
- **Used inside `unstable_cache()`.** `unstable_cache()` is a shared cache and has the same restriction.
- **Used without an active request.** A private cached function depends on the current request, so it cannot run during build-time contexts such as `generateStaticParams`.

```jsx filename="app/page.js" highlight={2,7}
async function PrivateSegment() {
'use cache: private'
return <p>Private</p>
}

export default async function Page() {
'use cache' // A public cache cannot contain a private one
return <PrivateSegment />
}
```

## Possible Ways to Fix It

Nest a private cached function only inside another private cached function. You can also call it directly from a component or function that runs during a request. Do not place it inside a public cached function, `unstable_cache()`, or a build-time context.

```jsx filename="app/page.js" highlight={7}
async function PrivateSegment() {
'use cache: private'
return <p>Private</p>
}

export default async function Page() {
// No surrounding "use cache". The private segment runs per request.
return <PrivateSegment />
}
```

If the data is the same for every user, use the `use cache` directive so Next.js can prerender and reuse the result. If you need per-user data, read it during the request instead of in `generateStaticParams`.

## Useful Links

- [`use cache: private` directive](/docs/app/api-reference/directives/use-cache-private)
- [`use cache` directive](/docs/app/api-reference/directives/use-cache)
16 changes: 4 additions & 12 deletions evals/evals/agent-043-view-transitions/EVAL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* product gallery app covering shared element morphs, directional navigation,
* Suspense reveal animations, and accessibility.
*
* View transitions need no next.config flag: experimental.viewTransition was
* inert and removed in #96098, and the docs now say "works with no
* configuration" — so nothing here may grade the config either way.
*
* Tricky because agents may:
* - Not know about the experimental.viewTransition flag in next.config
* - Try to call document.startViewTransition manually instead of using
* React's <ViewTransition> component
* - Import ViewTransition from a third-party library instead of 'react'
Expand Down Expand Up @@ -73,17 +76,6 @@ function readAllCssFiles(): string {
return unique.map((f) => readFileSync(f, 'utf-8')).join('\n')
}

test('next.config enables viewTransition', () => {
const configPath = existsSync(join(process.cwd(), 'next.config.ts'))
? 'next.config.ts'
: 'next.config.js'
const content = stripComments(
readFileSync(join(process.cwd(), configPath), 'utf-8')
)

expect(content).toMatch(/viewTransition\s*:\s*true/)
})

test('ViewTransition is imported from react', () => {
const allSource = readAllSourceFiles()

Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "16.4.0-canary.12"
"version": "16.4.0-canary.13"
}
2 changes: 1 addition & 1 deletion packages/create-next-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/devlow-bench/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@vercel/devlow-bench",
"private": true,
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"description": "Benchmarking tool for the developer workflow",
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"description": "ESLint configuration used by Next.js.",
"license": "MIT",
"repository": {
Expand All @@ -12,7 +12,7 @@
"dist"
],
"dependencies": {
"@next/eslint-plugin-next": "16.4.0-canary.12",
"@next/eslint-plugin-next": "16.4.0-canary.13",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-internal/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next/eslint-plugin-internal",
"private": true,
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"description": "ESLint plugin for working on Next.js.",
"exports": {
".": "./src/eslint-plugin-internal.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"description": "ESLint plugin for Next.js.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/font/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next/font",
"private": true,
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"repository": {
"url": "vercel/next.js",
"directory": "packages/font"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-bundle-analyzer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-codemod/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-env/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/env",
"version": "16.4.0-canary.12",
"version": "16.4.0-canary.13",
"keywords": [
"react",
"next",
Expand Down
Loading
Loading