The use cache: remote directive stores cached output in a durable cache handler instead of in-memory storage, so every server instance shares one cache. It requires Cache Components and is useful when in-memory entries keep getting evicted or re-run on each serverless request.
You add it the same way as regular use cache:
// app/lib/products.ts
import { cacheLife, cacheTag } from 'next/cache'
export async function getProductPrice(productId: string, currency: string) {
'use cache: remote'
cacheTag(`product-price-${productId}`)
cacheLife({ expire: 3600 })
const res = await fetch(`https://api.example.com/prices/${productId}?currency=${currency}`)
return res.json()
}The result now lives in a remote handler rather than the current instance's memory. In a serverless environment this means many instances can share one entry, so a price fetched once is not fetched again by the next instance. The storage backend is configured through cacheHandlers, though hosting providers usually set it up for you.
How it differs from the other directives
| Feature | use cache | use cache: remote | use cache: private |
|---|---|---|---|
| Server-side caching | In-memory or cache handler | Remote cache handler | None |
| Scope | Shared across users | Shared across users | Per browser |
| Reads cookies and headers | No | No | Yes |
| Extra costs | None | Storage and network | None |
The remote variant adds a network roundtrip for every lookup, so it only wins when that cost is cheaper than the backend work it skips.
When remote caching pays off
- Your upstream API has rate limits or request quotas you risk hitting.
- Your database or CMS becomes a bottleneck under high traffic.
- The operation is expensive to run repeatedly, such as an aggregate report.
- The upstream service is flaky and you want to absorb failures.
- Serverless instances have ephemeral memory, so most in-memory lookups miss.
For static shell content, regular use cache is usually enough. Remote caching earns its cost when content is deferred to request time and many instances would otherwise miss the in-memory cache.
Cache key considerations
Hit rate depends on keys repeating. Cache on a dimension with few values, then filter in memory for the rest.
// app/components/welcome-message.tsx
import { cookies } from 'next/headers'
import { cacheLife } from 'next/cache'
export async function WelcomeMessage() {
const language = (await cookies()).get('language')?.value || 'en'
const content = await getCMSContent(language)
return <p>{content.welcomeMessage}</p>
}
async function getCMSContent(language: string) {
'use cache: remote'
cacheLife({ expire: 3600 })
const res = await fetch(`https://cms.example.com/home?language=${language}`)
return res.json()
}The remote entry is keyed by language, not by user. A handful of languages produces a handful of shared entries, while keying by user ID would produce thousands of entries that almost never repeat.
WelcomeMessage itself reads cookies, so it renders at request time and the route must place it inside a Suspense boundary. That is exactly the situation remote caching is built for, because the in-memory cache rarely helps once a component is deferred past the static shell.
The same idea applies to price filters: cache per category and filter by price in memory rather than creating an entry per price value. Read cookies outside the cached scope and pass the extracted value in as an argument.
Nesting rules
The remote directive follows specific nesting rules.
- Remote inside remote is allowed.
- Remote inside regular use cache is allowed.
- Remote inside private is not allowed.
- Private inside remote is not allowed.
The rule exists because private results never leave the browser, so a shared entry cannot be built inside one. Keep the combinations in mind when a cached component calls another cached helper.
Persistence across deploys
Remote entries do not survive a new deploy. The cache key includes the build ID or deploymentId, so a fresh build produces fresh keys and the old entries become unreachable.
Between builds, a function's identity hash or the shape of its return value can change, so reusing old entries would risk serving stale or malformed data. For data that must persist across deploys, use the fetch cache or unstable_cache instead.
Common mistakes
- Keying on user-specific values, which destroys the shared hit rate.
- Using remote for static shell content where regular use cache already works.
- Expecting remote entries to carry over between deploys.
For the per-user browser-only case, see the private variant. For the directive's scopes, see the use cache directive. For invalidating tagged entries, see cacheTag explained.
Rune AI
Key Insights
- use cache: remote stores output in a durable shared cache handler.
- It trades infrastructure cost and lookup latency for higher hit rates.
- Cache keys should repeat, so cache on dimensions with few values.
- Remote entries do not persist across deploys.
- Remote can nest inside remote or use cache, but not private.
Frequently Asked Questions
Do remote cache entries survive a new deploy?
When should I avoid use cache: remote?
Conclusion
use cache: remote moves cached output into a durable handler shared across instances. It pays off at high traffic with slow or rate-limited backends, but only when cache keys repeat. Keep keys on low-cardinality dimensions and prefer regular use cache for static shell content.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.