Private and remote caches
Cache per-user results with 'use cache: private', share results across servers with 'use cache: remote' and cacheHandlers, and keep personal data safe.
- 01Cache a visitor's own dataUse 'use cache: private' to read cookies inside a cached function, and say where its result is kept: one request on the server, and the browser.
- 02Share results across serversUse 'use cache: remote' for data many visitors share, and give it a store with cacheHandlers, knowing what happens without one.
- 03Keep personal data out of shared storesSpot per-user data on its way into a shared cache, read what such a store holds, and choose the cache that fits each part of a page.
Orders that belong to one visitor
The 'use cache' directive stores what a function returns, and gives the same inputs the same stored copy. That copy is shared by every request, so a plain "use cache" function can't read cookies(): reading it there is an error. The tea shop's orders page needs exactly that. It shows the signed-in visitor's own orders, and the shop knows who is visiting from a session cookie: a cookie that holds a session id, a value the shop gave the visitor when they signed in.
Next.js has two more versions of the directive. "use cache: private" is for results that belong to one visitor. "use cache: remote" is for results that many visitors share, kept in a store of their own, which every server can share once a cache handler, or your hosting platform, supplies one. Here is the private one. It's a real Next.js 16.4.3 project, built with next build, run with next start, and recorded:
lib/orders.tsimport { cookies } from "next/headers"; const api = process.env.SHOP_API_URL; export type Order = { id: number; tea: string; boxes: number }; export async function getMyOrders(): Promise<Order[]> { "use cache: private"; const session = (await cookies()).get("session")?.value; const response = await fetch(`${api}/orders?session=${session}`); return response.json();}You have 2 orders.
- 2 boxes of Mint tea
- 3 boxes of Green tea
Sent with cookie: session=asha-7f3k. The page as the browser shows it when its HTML arrives, drawn with plain styles. Its JavaScript doesn't run here.
In lib/orders.ts, the file shown first, getMyOrders on lines 7 to 12 asks the shop's API for the visitor's orders. Line 8, "use cache: private", makes it a private cache. Line 9 reads the session cookie inside the cached function, which a plain "use cache" doesn't allow. Line 10 sends the session to the API. Open app/orders/page.tsx: OrderCount on lines 16 to 19 and OrderList on lines 21 to 32 both call getMyOrders, inside the Suspense on lines 8 to 11.
The API is a stand-in that ran on the same computer and kept every request it received. Three visits came in: Asha, Asha again, and Ben, each with their own session cookie. Pick each one above the page in the Browser tab. Asha sees 2 boxes of Mint tea and 3 boxes of Green tea; Ben sees his own orders. The three visits caused three API calls, one each, even though two components asked on each visit. (The recording's prefetches, further down, asked again.) The Terminal shows ◐ /orders: the heading is in the static shell, and the orders stream in for each request.
"use cache" uses each server's memory. "use cache: remote" uses a shared store once a handler is configured, and the same memory when none is. "use cache: private" keeps nothing on the server after the request, and the visitor's browser keeps what the router fetched.A tea chain has three places to keep things. Tins any customer may buy go on a shelf behind the counter, and each branch has its own shelf. Big orders go in a storeroom every branch shares. A customer's own order is written on a note on their tray, and the staff throw the note away when the order is served. The customer leaves with a receipt, which nobody else sees.
- In real life: A shelf behind the counter of one branch, for things any customer may get
- In Next.js:
"use cache"with the built-in handler: each server's own memory - In real life: The storeroom every branch of the chain shares
- In Next.js:
"use cache: remote"with a configured handler - In real life: A note on one customer's tray, thrown away when the order is done
- In Next.js:
"use cache: private"on the server: one request - In real life: The receipt the customer takes home
- In Next.js: What the router keeps in that visitor's browser
"use cache: private" caches a result that belongs to one visitor; "use cache: remote" caches a result many visitors share, in a store all servers can share once a handler, or your host, provides one. A private cache may read cookies(), headers() and searchParams, and keeps nothing on the server after the request. A remote cache can't read them, and its store is set in cacheHandlers.
What a private cache reads, and where it keeps results
A private cache is allowed to read request data that no shared cache may read. Each row is one request-time API (Request-time APIs teaches them), as Next.js's docs list them:
| API | Inside "use cache" or "use cache: remote" | Inside "use cache: private" |
|---|---|---|
cookies() | No. The build stops: read it outside and pass the value in. | Yes. |
headers() | No, for the same reason. | Yes. |
searchParams | No, for the same reason. | Yes. |
connection() | No. | No. It's about one connection, which no cache may keep. |
On the server: one request
The three visits asked the API three times. So where did the private result go? In production, nowhere that lasts. Next.js keeps it only for the request that made it. The docs say calls with the same inputs during one request can share one result, and the recording fits: OrderCount and OrderList both asked, and the API answered once per visit. When the response is sent, the result is gone from the server. In Next.js's own code, the private branch picks no cache handler at all:
if (isPrivate) {
// Private caches normally go to the Resume Data Cache (RDC), not a cache
// handler. In development we additionally persist them in a dedicated
// built-in in-memory handler so that reloads are fast.
if (process.env.__NEXT_DEV_SERVER) {
cacheHandler = getPrivateCacheHandler()
}Line 1 checks for a private cache. Lines 2 to 4 say where its result goes instead: the Resume Data Cache, a store that belongs to one render of one request. Lines 5 to 7 add one exception, for next dev only: there, Next.js also keeps private results in memory so a reload is fast. The docs say that development store is keyed by the request's cookies and headers, so a visitor with other cookies gets another entry. (These are lines 1935 to 1941, and the dev store is made in handlers.ts, lines 126 to 133.)
The orders are also left out of the static shell. In the Network tab above, the HTML for each visit starts with Loading your orders…, and the orders arrive later in the same response. The response's Cache-Control: private, no-cache, no-store… tells every cache between the server and the browser, like a CDN, not to keep it.
In the browser: a prefetch made for one visitor
The private result has one more place to live: the visitor's browser. These steps were taken in real Chrome against the same app. Asha signs in (the shop's stand-in sign-in sets her session cookie and sends her to /), waits a moment, and clicks Your orders. Open the Steps tab:
lib/orders.tsimport { cookies } from "next/headers"; const api = process.env.SHOP_API_URL; export type Order = { id: number; tea: string; boxes: number }; export async function getMyOrders(): Promise<Order[]> { "use cache: private"; const session = (await cookies()).get("session")?.value; const response = await fetch(`${api}/orders?session=${session}`); return response.json();}Each step was taken in real Chrome against next start. Pick one to see the page after it.
You have 2 orders.
- 2 boxes of Mint tea
- 3 boxes of Green tea
- Focus
- the link “Your orders”
- Scrolled
- 0 px down
No requests during this step: the page already had what it needed
- Steps 1 and 2. The home page loads with the
Your orderslink on the screen, and the router prefetches the orders page. One of its requests has the headernext-router-prefetch: 3: in Next.js 16.4.3, that's the router asking for a prefetch rendered on the server for this visitor, with her cookie. Step 2 waits until that request has answered. - Step 3, the click. The orders appear, and the step made no request at all: everything came with the prefetch.
- Steps 4 and 5. Home, then Your orders again. Still no request: the router showed the orders from what it kept in the browser's memory.
The recorder also sent that prefetch on its own, twice, with the same headers Chrome sent: once with Asha's cookie and once with Ben's. In the Network tab, pick the prefetch for Asha, then the prefetch for Ben. Each answer is an RSC payload (the description of the rendered page that React uses on a client-side navigation), and each holds only that visitor's orders: "Mint tea" and "Green tea" for Asha, "Lemon tea" and "Mint tea" for Ben. The docs call this a route's App Shell, the part a link prefetches; for a route that reads cookies, it's made for each session and kept on the client for that session. Instant navigations and partial prefetching takes prefetching apart.
How long may the browser keep it? The docs say the router can keep a private result for its stale time, set with cacheLife (Cache lifetimes and tags teaches it). getMyOrders doesn't call cacheLife, so it gets the default profile's 5 minutes. Below 30 seconds, it isn't prefetched at all; below 5 minutes, it isn't part of the App Shell. A reload clears the router's memory. And unlike the other two kinds, a private cache can't be given a store of your own.
'use cache: remote' and cacheHandlers
A remote cache earns its place with data that many visitors share, read at request time. The shop's menu shows prices in the visitor's currency, from a currency cookie. Only a few currencies exist, and the prices aren't personal:
import { cacheLife } from "next/cache";
const api = process.env.SHOP_API_URL;
export type MenuItem = { name: string; price: string };
export async function getMenu(currency: string): Promise<MenuItem[]> {
"use cache: remote";
cacheLife("hours");
const response = await fetch(`${api}/menu?currency=${currency}`);
return response.json();
}
Line 8 marks getMenu with "use cache: remote". Line 9 gives it the hours lifetime, and line 10 asks the API for one currency. The page reads the cookie outside the cache and passes it in:
import { Suspense } from "react";
import { cookies } from "next/headers";
import { getMenu } from "@/lib/menu";
export default function MenuPage() {
return (
<main>
<h1>Menu</h1>
<Suspense fallback={<p>Loading prices…</p>}>
<Prices />
</Suspense>
</main>
);
}
async function Prices() {
const currency = (await cookies()).get("currency")?.value ?? "usd";
const menu = await getMenu(currency);
return (
<ul>
{menu.map((item) => (
<li key={item.name}>
{item.name}: {item.price}
</li>
))}
</ul>
);
}
Line 17 reads the cookie, with "usd" when there's none, and line 18 passes it to getMenu. Lines 9 to 11 put Prices in Suspense, because it waits for the request. Three visitors came: Asha and Ben in dollars, Chen in euros. Pick a version:
lib/menu.tsimport { cacheLife } from "next/cache"; const api = process.env.SHOP_API_URL; export type MenuItem = { name: string; price: string }; export async function getMenu(currency: string): Promise<MenuItem[]> { "use cache: remote"; cacheLife("hours"); const response = await fetch(`${api}/menu?currency=${currency}`); return response.json();}$ next build▲ Next.js 16.4.3 (Turbopack)- Cache Components enabled- Partial Prefetching enabled Creating an optimized production build ...✓ Compiled successfully Running TypeScript ... Finished TypeScript ... Collecting page data ...✓ Generating static pages (4/4) Finalizing page optimization ... Route (app)┌ ○ /├ ○ /_not-found└ ◐ /menu ○ (Static) prerendered as static content◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed contentlib/menu.ts marks getMenu with "use cache: remote" on line 8, and next.config.ts is the one create-next-app writes, with no cacheHandlers. Three visits, two currencies, and the API was asked twice: Ben's dollars came from Asha's copy. The copy lived in this server's memory, in the same store plain "use cache" uses.
Both versions asked the API twice: once for dollars, once for euros. Ben's dollars came from Asha's copy. So what does a handler change? Where the copy lives.
Without a handler: the same memory as 'use cache'
"use cache: remote" only names a kind of store, called remote. If nothing provides one, Next.js uses its built-in store for both kinds. This is the code that sets them up when nothing is configured:
} else {
const handler = createDefaultCacheHandler(builtInSize)
builtInHandlers.add(handler)
debug?.('setting "default" cache handler from default')
handlersMap.set('default', handler)
debug?.('setting "remote" cache handler from default')
handlersMap.set('remote', handler)
}Line 2 makes one in-memory store. Line 6 uses it for default, the kind plain "use cache" uses, and line 8 uses the same store for remote (handlers.ts, lines 106 to 114). So without a handler, a remote entry lives in one server's memory, like any other. A shop on three servers would ask the API for dollars up to three times. The docs add that hosting platforms usually provide a remote store for you; when you host the app yourself, you configure it.
"use cache: remote" only names a kind of store. With cacheHandlers.remote set, your handler gets every get and set. Without it, Next.js 16.4.3 uses the in-memory store that "use cache" uses, one per server.With a handler: your store
The second version sets a handler for remote in next.config.ts:
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
cacheComponents: true,
partialPrefetching: true,
cacheHandlers: {
remote: require.resolve("./cache-handlers/remote.js"),
},
};
export default nextConfig;
Lines 6 to 8 are cacheHandlers. Its keys are kinds of store: remote here, default to replace the store of plain "use cache", or a name of your own, like sessions, which code then uses with "use cache: sessions". Each value is the path of a file that exports the handler. Don't mix it up with cacheHandler, singular: an older option for the store of the incremental cache, which keeps prerendered pages for Incremental Static Regeneration, Route Handler responses and optimized images. The docs say "use cache" never uses it. This is the tea shop's printing handler:
// A stand-in for a store every server shares, like Redis. It keeps
// entries in a Map and prints what Next.js hands it.
const store = new Map();
function streamOf(bytes) {
return new ReadableStream({
start(controller) {
controller.enqueue(bytes);
controller.close();
},
});
}
module.exports = {
async get(cacheKey) {
const entry = store.get(cacheKey);
console.log(entry ? "remote get, found:" : "remote get, missing:", cacheKey);
return entry ? { ...entry, value: streamOf(entry.value) } : undefined;
},
async set(cacheKey, pendingEntry) {
const entry = await pendingEntry;
const value = new Uint8Array(await new Response(entry.value).arrayBuffer());
store.set(cacheKey, { ...entry, value });
console.log("remote set:", cacheKey, new TextDecoder().decode(value));
},
async refreshTags() {},
async getExpiration() {
return 0;
},
async updateTags() {},
};
Line 3 is the whole store: a Map in memory, standing in for Redis. get, on lines 15 to 19, looks the key up, prints whether it found it, and hands the stored bytes back as a new stream. set, on lines 20 to 25, waits for the entry on line 21, reads its value stream into bytes on line 22 (a stream can be read only once), keeps them on line 23, and prints them on line 24. Lines 26 to 30 are the tag methods, which this handler doesn't need. In the playground's Terminal, the handler printed remote get, missing and remote set for dollars, then remote get, found for Ben, then a miss and a set for euros. Each row is one method a handler has:
| Method | What it does | When Next.js calls it |
|---|---|---|
get(cacheKey, softTags) | Returns the stored entry for a key, or undefined when there's none. | Each time a cached call runs, before the function body. |
set(cacheKey, pendingEntry) | Stores a new entry. Its value arrives as a stream, which can be read only once. | After a miss, once the function has run. |
refreshTags() | Lets a handler learn which tags other servers expired. | Once per request, before the first read from this handler. |
getExpiration(tags) | Says when any of these tags was last expired. | When Next.js checks whether an entry is still good. |
updateTags(tags, durations) | Marks tags as expired. | When code calls revalidateTag, updateTag or revalidatePath. |
A real remote handler sends these to a shared store, and the docs point at a fuller Redis example. The key holds the build's ID (or a deployment ID, if you set one), so after a deploy the new build can't reach the old entries: remote entries don't survive a deploy, by design. The docs also give rules for nesting: a remote cache may call another remote cache or be called by a plain one, but a private cache and a remote cache can't call each other.
A shared store costs money and adds a network trip to every read. Each row is a question to ask before you reach for one:
| Question | Remote helps when | Remote doesn't help when |
|---|---|---|
| The data is read at request time, outside the static shell. | Yes. Each server would otherwise fill its own copy. | Not needed: a prerendered shell already holds it. |
| Few values go into the key, like a currency. | Yes. Many visitors share each entry. | No, if every visitor makes a new key: almost nothing is reused. |
| The data source is slow, expensive or rate-limited. | Yes. One call fills the entry for every server. | Less useful when the source is already fast. |
| The data changes every few seconds. | Rarely. Entries go out of date before anyone reuses them. | Read it fresh instead. |
Where cached data lives
Put together, a cached result can be in five places. Each row is one place, what puts data there, and how long it may stay:
| Place | What it holds | How long |
|---|---|---|
| The build's output | Results cached while next build prerendered a route: they're part of that route's saved output, and a ◐ route's saved data can carry some to its requests. | Until the next build. |
| Each server's memory | "use cache", and "use cache: remote" without a handler of its own, at request time: one store in each server process, 50 MB by default (cacheMaxMemorySize). | Until the process stops, the store is full, or the entry's lifetime ends. |
| A store your handler talks to | "use cache: remote" with cacheHandlers.remote, or "use cache" with cacheHandlers.default: one store every server can share, like Redis. | Until it expires, or your handler removes it. A new build's keys can't reach it. |
| One request | "use cache: private" on the server, in production: calls with the same inputs share one result while the request lasts. | Until the response is sent. |
| The visitor's browser | What the router fetched for that visitor, private results included, kept in memory. | Until its stale time ends, or the page reloads. |
cacheLife sets the lifetimes, and each one says what may happen. stale is how long the browser may reuse a copy without asking. revalidate is when the server may serve the old copy and make a new one in the background. expire is when a request must wait for a new one. A store may still drop an entry sooner: in 16.4.3, the built-in in-memory store, in production, throws an entry away once it's past revalidate, so the next call misses and fills it again (default.ts, lines 114 to 116). That's how this version is built, not a promise of the API. A handler of your own decides for itself.
Two things don't move between places. The memory of the next build process isn't carried into next start; what the build cached lives on in the output it prerendered, which is how a shell can hold a cached result without asking again (The 'use cache' directive showed one). And nothing private ever reaches the server's stores at all.
Choosing a cache for each part of the shop
A real page mixes all of these. Ask two questions about each piece of data: whose is it, and how many different values go into its key? Sort the shop's data:
- A visitor's order history, read with their session cookie.
- Teas picked for the visitor by a session helper that reads cookies deep inside its own code.
- Menu prices in the visitor's currency, read after a cookie, on a site that runs on many servers.
- A rate-limited supplier's tea list, needed at request time by every server.
- The tea of the day, the same for everyone, shown on a prerendered home page.
- A tea's description, which changes once a month.
- Live stock counts that must be fresh on every visit.
- A check of whether the visitor's session token is still valid right now.
Sort each piece of the tea shop's data into the cache that fits it, or no cache.
Common mix-ups
- "A private cache stores each visitor's copy on the server." No. In production it keeps nothing on the server after the request. The three visits asked the API three times.
- "'use cache: remote' shares entries between servers on its own." No. Without
cacheHandlers.remote, or a store your hosting platform provides, it uses each server's own memory, like"use cache". - "Passing the session as an argument makes a shared cache safe." No. It keeps visitors' entries apart, but the session and the data still sit in plain text in a store every server reads.
- "A private result is prerendered for each visitor." No. The build can't know who will visit. Private results are left out of the static shell and made at request time, or in a prefetch made for that visitor.
- "A private cache may read anything about the request." No.
cookies(),headers()andsearchParamsare allowed;connection()isn't. - "Remote entries survive a deploy." No. The key holds the build's ID, so a new build can't reach them.
Practice exercises
These exercises are about Next.js projects, which can't run inside this page. Answer first, then use the hints.
The orders page calls this getMyOrders from two components. Asha visits, Asha visits again, then Ben visits. How many times does the shop's API answer?
import { cookies } from "next/headers";
const api = process.env.SHOP_API_URL;
export type Order = { id: number; tea: string; boxes: number };
export async function getMyOrders(): Promise<Order[]> {
"use cache: private";
const session = (await cookies()).get("session")?.value;
const response = await fetch(`${api}/orders?session=${session}`);
return response.json();
}
3. Asha, Asha again and Ben each made one call: the two components shared one result inside each request, and nothing was kept between requests. The recording's API received /orders?session=asha-7f3k twice and /orders?session=ben-2m9q once.
This file stops next build. Which call is the problem?
import { cookies } from "next/headers";
const api = process.env.SHOP_API_URL;
export type Order = { id: number; tea: string; boxes: number };
export async function getMyOrders(): Promise<Order[]> {
"use cache: remote";
const session = (await cookies()).get("session")?.value;
const response = await fetch(`${api}/orders?session=${session}`);
return response.json();
}
cookies() on line 9. A remote cache is shared like "use cache", so the build stops: cookies() can't be read inside "use cache". Read it outside and pass a value in, or use "use cache: private" if the data belongs to one visitor.
This is what the printing handler received when getOrders(session) ran for Asha. Which text in the key belongs to her?
remote set: ["c0fb…",["asha-7f3k"],["l4Oud…"]] 0:"$@1"
1:[{"id":1,"tea":"Mint tea","boxes":2},{"id":2,"tea":"Green tea","boxes":3}]asha-7f3k, Asha's session, sitting in plain text in a store every server reads. The middle part of the key holds the arguments, and the session was the only one.
This version puts every visitor's session and orders in the shared store. Which directive should replace line 6, so the orders are never stored on the server?
const api = process.env.SHOP_API_URL;
export type Order = { id: number; tea: string; boxes: number };
export async function getOrders(session: string | undefined): Promise<Order[]> {
"use cache: remote";
const response = await fetch(`${api}/orders?session=${session}`);
return response.json();
}
import { cookies } from "next/headers";
const api = process.env.SHOP_API_URL;
export type Order = { id: number; tea: string; boxes: number };
export async function getMyOrders(): Promise<Order[]> {
"use cache: private";
const session = (await cookies()).get("session")?.value;
const response = await fetch(`${api}/orders?session=${session}`);
return response.json();
}
"use cache: private". With it, the function may read the cookie itself, so the session no longer goes into a key, and nothing is kept on the server after the request. It's the version the playground recorded: the handler printed nothing.
The shop runs this getMenu on three servers, with no cacheHandlers. Within an hour, each server gets a visitor who pays in dollars, and nothing else fills their memory. At most, how many times is the API asked for dollars?
import { cacheLife } from "next/cache";
const api = process.env.SHOP_API_URL;
export type MenuItem = { name: string; price: string };
export async function getMenu(currency: string): Promise<MenuItem[]> {
"use cache: remote";
cacheLife("hours");
const response = await fetch(`${api}/menu?currency=${currency}`);
return response.json();
}
Up to 3. With no handler, remote uses the same in-memory store as "use cache", one per server process. In the recording, one server asked for dollars once; three servers each fill their own copy. A configured remote handler would let them share one.
The shop adds a wishlist. A session helper the shop can't change reads the cookie inside its own code and returns the visitor's saved teas. The shop's rules say no visitor's wishlist may be stored on the server. Which directive fits?
"use cache: private". The wishlist belongs to one visitor, the helper reads cookies inside, so nothing can be lifted out and passed, and the shop's rules forbid storing it on the server. A private cache keeps it only for the request on the server, and in the visitor's browser.
Check your understanding
For each question, ask whose data it is, and where its copy is kept.
Question 1 of 7Which of these can a function marked
"use cache: private"read inside it?Choose an answer to see the explanation.
Question 2 of 7Asha opens her orders page twice. How many times does the shop's API answer for her?
Read the code, then predictTypeScriptimport { cookies } from "next/headers"; const api = process.env.SHOP_API_URL; export type Order = { id: number; tea: string; boxes: number }; export async function getMyOrders(): Promise<Order[]> { "use cache: private"; const session = (await cookies()).get("session")?.value; const response = await fetch(`${api}/orders?session=${session}`); return response.json(); }Choose an answer to see the explanation.
Question 3 of 7This function runs for Asha and then for Ben. What does the remote store's key hold?
Read the code, then predictTypeScriptconst api = process.env.SHOP_API_URL; export type Order = { id: number; tea: string; boxes: number }; export async function getOrders(session: string | undefined): Promise<Order[]> { "use cache: remote"; const response = await fetch(`${api}/orders?session=${session}`); return response.json(); }Choose an answer to see the explanation.
Question 4 of 7A site has no
cacheHandlersand runs on three servers. Where do"use cache: remote"entries go?Choose an answer to see the explanation.
Question 5 of 7Which kinds of cache can
cacheHandlersinnext.config.tsgive a store of their own?Choose an answer to see the explanation.
Question 6 of 7After signing in, Asha clicks Your orders and the page shows her orders with no request at all. Why?
Choose an answer to see the explanation.
Question 7 of 7Why is
getMenu(currency)a better fit for"use cache: remote"thangetOrders(session)?Read the code, then predictTypeScriptimport { cacheLife } from "next/cache"; const api = process.env.SHOP_API_URL; export type MenuItem = { name: string; price: string }; export async function getMenu(currency: string): Promise<MenuItem[]> { "use cache: remote"; cacheLife("hours"); const response = await fetch(`${api}/menu?currency=${currency}`); return response.json(); }Choose an answer to see the explanation.
Key takeaways
"use cache: private"may readcookies(),headers()andsearchParams. In production it keeps its result for one request on the server, and the router may keep it in that visitor's browser.- A route that reads cookies gets a prefetch made for each visitor. With a private cache inside, the click can need no request at all.
- Per-user data doesn't belong in a shared cache. Passing the session in keeps entries apart, but stores sessions and personal data in plain text, readable by every server, until they expire.
"use cache: remote"is for shared data read at request time, with few different keys. WithoutcacheHandlers.remote, it uses each server's memory, like"use cache".cacheHandlersgivesdefault,remoteor a named kind a store of your own, through five methods:get,set,refreshTags,getExpirationandupdateTags.- Remote entries don't survive a deploy, and a private cache can't be given a handler.
Remember the one-liner.
Shared data goes in a shared store; a visitor's own data stays with the visitor.
These links point at release v16.4.3, the version every example on this page was built with. handlers.ts sets up the stores, and default.ts is the built-in in-memory one. use-cache-wrapper.ts decides where each kind of cache reads and writes. The course's last stage, Under the hood, reads them.
Coming next: Instant navigations and partial prefetching. It shows what a link prefetches, how to prefetch more or less, and how to guarantee a route stays static.