Skip to content

Commit 7c70ff8

Browse files
authored
docs: edits to use-cache-remote (#85168)
1 parent 8c939dc commit 7c70ff8

File tree

1 file changed

+99
-49
lines changed

1 file changed

+99
-49
lines changed

docs/01-app/03-api-reference/01-directives/use-cache-remote.mdx

Lines changed: 99 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ related:
1111
- app/api-reference/config/next-config-js/cacheComponents
1212
- app/api-reference/functions/cacheLife
1313
- app/api-reference/functions/cacheTag
14+
- app/api-reference/functions/connection
1415
---
1516

16-
The `'use cache: remote'` directive enables caching in dynamic contexts where regular `use cache` would not work, such as after calling `await connection()`, `await cookies()`, or `await headers()`.
17+
The `'use cache: remote'` directive enables caching of **shared data** in dynamic contexts where regular [`use cache`](/docs/app/api-reference/directives/use-cache) would not work, for example after calling [`await connection()`](/docs/app/api-reference/functions/connection), [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers).
1718

18-
> **Good to know:** `'use cache: remote'` is a variant of [`use cache`](/docs/app/api-reference/directives/use-cache) designed specifically for runtime caching in dynamic contexts. Unlike regular `use cache`, it works after dynamic data access and stores results in server-side cache handlers.
19+
> **Good to know:**
20+
>
21+
> - Results are stored in server-side cache handlers and shared across all users.
22+
> - For **user-specific data** that depends on [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers), use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead to avoid accidentally sharing personalized data between users.
1923
2024
## Usage
2125

@@ -67,13 +71,13 @@ export default async function ProductPage({
6771
}
6872

6973
async function ProductPrice({ productId }: { productId: string }) {
70-
// Ensure this component is never cached in the static shell
71-
// by requiring a connection. This makes it dynamic.
74+
// Calling connection() makes this component dynamic, preventing
75+
// it from being included in the static shell. This ensures the price
76+
// is always fetched at request time.
7277
await connection()
7378

74-
// Since we're in a dynamic context (after connection()),
75-
// using "use cache" would NOT cache at all. We need "use cache: remote"
76-
// to cache at runtime in a remote cache handler.
79+
// Now we can cache the price in a remote cache handler.
80+
// Regular 'use cache' would NOT work here because we're in a dynamic context.
7781
const price = await getProductPrice(productId)
7882

7983
return <div>Price: ${price}</div>
@@ -82,8 +86,11 @@ async function ProductPrice({ productId }: { productId: string }) {
8286
async function getProductPrice(productId: string) {
8387
'use cache: remote'
8488
cacheTag(`product-price-${productId}`)
85-
cacheLife({ stale: 3600 }) // 1 hour
8689

90+
// Client uses cached value for 1 hour
91+
cacheLife({ stale: 3600 })
92+
93+
// This database query is cached and shared across all users
8794
return db.products.getPrice(productId)
8895
}
8996
```
@@ -107,13 +114,13 @@ export default async function ProductPage({ params }) {
107114
}
108115

109116
async function ProductPrice({ productId }) {
110-
// Ensure this component is never cached in the static shell
111-
// by requiring a connection. This makes it dynamic.
117+
// Calling connection() makes this component dynamic, preventing
118+
// it from being included in the static shell. This ensures the price
119+
// is always fetched at request time.
112120
await connection()
113121

114-
// Since we're in a dynamic context (after connection()),
115-
// using "use cache" would NOT cache at all. We need "use cache: remote"
116-
// to cache at runtime in a remote cache handler.
122+
// Now we can cache the price in a remote cache handler.
123+
// Regular 'use cache' would NOT work here because we're in a dynamic context.
117124
const price = await getProductPrice(productId)
118125

119126
return <div>Price: ${price}</div>
@@ -122,13 +129,16 @@ async function ProductPrice({ productId }) {
122129
async function getProductPrice(productId) {
123130
'use cache: remote'
124131
cacheTag(`product-price-${productId}`)
125-
cacheLife({ stale: 3600 }) // 1 hour
126132

133+
// Client uses cached value for 1 hour
134+
cacheLife({ stale: 3600 })
135+
136+
// This database query is cached and shared across all users
127137
return db.products.getPrice(productId)
128138
}
129139
```
130140

131-
> **Note:** Regular `use cache` will not cache anything when used in a dynamic context (after `await connection()`, `await cookies()`, `await headers()`, etc.). Use `'use cache: remote'` to enable runtime caching in these scenarios.
141+
> **Note:** Regular [`use cache`](/docs/app/api-reference/directives/use-cache) will not cache anything when used in a [dynamic context](/docs/app/getting-started/partial-prerendering#dynamic-rendering) (after [`await connection()`](/docs/app/api-reference/functions/connection), [`await cookies()`](/docs/app/api-reference/functions/cookies), [`await headers()`](/docs/app/api-reference/functions/headers), etc.). Use `'use cache: remote'` to enable runtime caching in these scenarios.
132142
133143
## Difference from `use cache` and `use cache: private`
134144

@@ -147,15 +157,33 @@ Next.js provides three caching directives, each designed for different use cases
147157

148158
### When to use each
149159

150-
- **`use cache`**: Use for static content that can be prerendered at build time and doesn't depend on request-specific data.
151-
- **`'use cache: remote'`**: Use when caching is needed after dynamic data access (`connection()`) or for per-request caching of shared data.
152-
- **`'use cache: private'`**: Use for user-specific content that needs to be runtime prefetched and depends on cookies, headers, or search params.
160+
Choose the right caching directive based on your use case:
161+
162+
**Use [`use cache`](/docs/app/api-reference/directives/use-cache) when:**
163+
164+
- Content can be [prerendered at build time](/docs/app/getting-started/partial-prerendering)
165+
- Content is shared across all users
166+
- Content doesn't depend on request-specific data
167+
168+
**Use `'use cache: remote'` when:**
169+
170+
- You need caching within [dynamic rendering](/docs/app/getting-started/partial-prerendering#dynamic-rendering)
171+
- Content is shared across users but must be rendered per-request
172+
- You want to cache expensive operations in a server-side cache handler
173+
174+
**Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) when:**
175+
176+
- Content is personalized per-user (depends on cookies, headers)
177+
- You need [runtime prefetching](/docs/app/guides/prefetching) of user-specific content
178+
- Content should never be shared between users
153179

154180
## How it works
155181

182+
The `'use cache: remote'` directive enables runtime caching of shared data in dynamic contexts by storing results in server-side cache handlers rather than prerendering at build time.
183+
156184
### Dynamic context detection
157185

158-
When Next.js encounters certain APIs like `connection()`, `cookies()`, or `headers()`, the context becomes "dynamic". In a dynamic context:
186+
When Next.js encounters certain APIs like [`connection()`](/docs/app/api-reference/functions/connection), [`cookies()`](/docs/app/api-reference/functions/cookies), or [`headers()`](/docs/app/api-reference/functions/headers), the context becomes "[dynamic](/docs/app/getting-started/partial-prerendering#dynamic-rendering)". In a dynamic context:
159187

160188
1. **Regular `use cache` stops working** - it won't cache anything
161189
2. **`'use cache: remote'` continues to work** - it caches in the remote cache handler
@@ -176,21 +204,21 @@ This means:
176204

177205
1. Cached data is shared across all users and requests
178206
2. Cache entries persist beyond a single session
179-
3. Cache invalidation works via `cacheTag` and `revalidateTag`
180-
4. Cache expiration is controlled by `cacheLife` configuration
207+
3. Cache invalidation works via [`cacheTag`](/docs/app/api-reference/functions/cacheTag) and [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)
208+
4. Cache expiration is controlled by [`cacheLife`](/docs/app/api-reference/functions/cacheLife) configuration
181209

182210
### Dynamic context example
183211

184212
```tsx
185213
async function UserDashboard() {
186-
// This call makes the context dynamic
214+
// Calling connection() makes the context dynamic
187215
await connection()
188216

189-
// Regular 'use cache' would NOT cache here
190-
const stats = await getStats() // Not cached with 'use cache'
217+
// Without any caching directive, this runs on every request
218+
const stats = await getStats()
191219

192-
// But 'use cache: remote' WILL cache here
193-
const analytics = await getAnalytics() // Cached with 'use cache: remote'
220+
// With 'use cache: remote', this is cached in the remote handler
221+
const analytics = await getAnalytics()
194222

195223
return (
196224
<div>
@@ -202,21 +230,25 @@ async function UserDashboard() {
202230

203231
async function getAnalytics() {
204232
'use cache: remote'
205-
cacheLife({ stale: 300 }) // 5 minutes
233+
234+
// Client uses cached value for 5 minutes
235+
cacheLife({ stale: 300 })
236+
237+
// This expensive operation is cached and shared across all requests
206238
return fetchAnalyticsData()
207239
}
208240
```
209241

210242
## Request APIs and remote caches
211243

212-
While `'use cache: remote'` technically allows access to request-specific APIs like `cookies()` and `headers()`, it's generally not recommended to use them together:
244+
While `'use cache: remote'` technically allows access to request-specific APIs like [`cookies()`](/docs/app/api-reference/functions/cookies) and [`headers()`](/docs/app/api-reference/functions/headers), it's generally not recommended to use them together:
213245

214-
| API | Allowed in `use cache` | Allowed in `'use cache: remote'` | Recommended |
215-
| -------------- | ---------------------- | -------------------------------- | ---------------------------------- |
216-
| `cookies()` | No | Yes (not recommended) | Use `'use cache: private'` instead |
217-
| `headers()` | No | Yes (not recommended) | Use `'use cache: private'` instead |
218-
| `connection()` | No | Yes | Yes - this is the primary use case |
219-
| `searchParams` | No | Yes (not recommended) | Use `'use cache: private'` instead |
246+
| API | Allowed in `use cache` | Allowed in `'use cache: remote'` | Recommended |
247+
| ------------------------------------------------------------------------------------- | ---------------------- | -------------------------------- | ------------------------------------------------------------------------------------------ |
248+
| [`cookies()`](/docs/app/api-reference/functions/cookies) | No | Yes (not recommended) | Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead |
249+
| [`headers()`](/docs/app/api-reference/functions/headers) | No | Yes (not recommended) | Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead |
250+
| [`connection()`](/docs/app/api-reference/functions/connection) | No | Yes | Yes - this is the primary use case |
251+
| [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) | No | Yes (not recommended) | Use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead |
220252

221253
> **Important:** If you need to cache based on cookies, headers, or search params, use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead. Remote caches are shared across all users, so caching user-specific data in them can lead to incorrect results being served to different users.
222254
@@ -302,9 +334,12 @@ export default async function DashboardPage() {
302334
async function getGlobalStats() {
303335
'use cache: remote'
304336
cacheTag('global-stats')
305-
cacheLife({ stale: 60 }) // 1 minute
306337

307-
// Expensive database query - cached for all users
338+
// Client uses cached value for 1 minute
339+
cacheLife({ stale: 60 })
340+
341+
// This expensive database query is cached and shared across all users,
342+
// reducing load on your database
308343
const stats = await db.analytics.aggregate({
309344
total_users: 'count',
310345
active_sessions: 'count',
@@ -346,9 +381,11 @@ async function FeedItems() {
346381
async function getFeedItems() {
347382
'use cache: remote'
348383
cacheTag('feed-items')
349-
cacheLife({ stale: 120 }) // 2 minutes
350384

351-
// Fetch from external API
385+
// Client uses cached value for 2 minutes
386+
cacheLife({ stale: 120 })
387+
388+
// This API call is cached, reducing requests to your external service
352389
const response = await fetch('https://api.example.com/feed')
353390
return response.json()
354391
}
@@ -373,9 +410,12 @@ export default async function ReportsPage() {
373410

374411
async function generateReport() {
375412
'use cache: remote'
376-
cacheLife({ stale: 3600 }) // 1 hour
377413

378-
// Expensive computation - shared across all authorized users
414+
// Client uses cached value for 1 hour
415+
cacheLife({ stale: 3600 })
416+
417+
// This expensive computation is cached and shared across all authorized users,
418+
// avoiding repeated calculations
379419
const data = await db.transactions.findMany()
380420

381421
return {
@@ -395,27 +435,35 @@ import { Suspense } from 'react'
395435
import { cookies, connection } from 'next/server'
396436
import { cacheLife, cacheTag } from 'next/cache'
397437

398-
// Static product data (build-time cache)
438+
// Static product data - prerendered at build time
399439
async function getProduct(id: string) {
400440
'use cache'
401441
cacheTag(`product-${id}`)
442+
443+
// This is cached at build time and shared across all users
402444
return db.products.find({ where: { id } })
403445
}
404446

405-
// Shared pricing data (runtime remote cache)
447+
// Shared pricing data - cached at runtime in remote handler
406448
async function getProductPrice(id: string) {
407449
'use cache: remote'
408450
cacheTag(`product-price-${id}`)
409-
cacheLife({ stale: 300 }) // 5 minutes
451+
452+
// Client uses cached value for 5 minutes
453+
cacheLife({ stale: 300 })
454+
455+
// This is cached at runtime and shared across all users
410456
return db.products.getPrice({ where: { id } })
411457
}
412458

413-
// User-specific recommendations (private cache)
459+
// User-specific recommendations - private cache per user
414460
async function getRecommendations(productId: string) {
415461
'use cache: private'
416462
cacheLife({ stale: 60 })
417463

418464
const sessionId = (await cookies()).get('session-id')?.value
465+
466+
// This is cached per-user and never shared
419467
return db.recommendations.findMany({
420468
where: { productId, sessionId },
421469
})
@@ -445,7 +493,9 @@ export default async function ProductPage({ params }) {
445493
}
446494

447495
async function ProductPriceComponent({ productId }) {
448-
await connection() // Make dynamic
496+
// Make this component dynamic
497+
await connection()
498+
449499
const price = await getProductPrice(productId)
450500
return <div>Price: ${price}</div>
451501
}
@@ -459,10 +509,10 @@ async function ProductRecommendations({ productId }) {
459509
> **Good to know**:
460510
>
461511
> - Remote caches are stored in server-side cache handlers and shared across all users
462-
> - Remote caches work in dynamic contexts where regular `use cache` would fail
463-
> - Use `cacheTag()` and `revalidateTag()` to invalidate remote caches on-demand
464-
> - Use `cacheLife()` to configure cache expiration
465-
> - For user-specific data, use `'use cache: private'` instead of `'use cache: remote'`
512+
> - Remote caches work in [dynamic contexts](/docs/app/getting-started/partial-prerendering#dynamic-rendering) where regular [`use cache`](/docs/app/api-reference/directives/use-cache) would fail
513+
> - Use [`cacheTag()`](/docs/app/api-reference/functions/cacheTag) and [`revalidateTag()`](/docs/app/api-reference/functions/revalidateTag) to invalidate remote caches on-demand
514+
> - Use [`cacheLife()`](/docs/app/api-reference/functions/cacheLife) to configure cache expiration
515+
> - For user-specific data, use [`'use cache: private'`](/docs/app/api-reference/directives/use-cache-private) instead of `'use cache: remote'`
466516
> - Remote caches reduce origin load by storing computed or fetched data server-side
467517
468518
## Platform Support

0 commit comments

Comments
 (0)