unstable_cache & React cache()
Cache expensive function results per-request or across requests with Next.js cache utilities.
unstable_cache & React cache() is a free React Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Two Cache Utilities
Next.js provides two complementary caching utilities: unstable_cache for persistent cross-request caching, and React's cache() for per-request deduplication.
React cache() — Per-Request Dedup
cache(fn) from React memoizes a function's result for the duration of a single server render pass. Two Server Components calling the same cached function get the same result without a second DB/API call.
import { cache } from 'react';
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
// Both components call getUser(id) — only one DB query fires per request
// component A: const user = await getUser('1');
// component B: const user = await getUser('1'); // uses cached resultunstable_cache — Persistent Cache
unstable_cache(fn, keyParts, options) persists the result across requests until explicitly revalidated. Think of it as fetch({ next: { revalidate } }) but for any async function (DB queries, Redis calls, etc.).
import { unstable_cache } from 'next/cache';
const getProducts = unstable_cache(
async () => db.products.findMany(),
['products-list'], // cache key parts
{ revalidate: 60, tags: ['products'] } // options
);
// In a Server Component:
const products = await getProducts();Cache Key Parts
The second argument to unstable_cache is an array of strings forming the cache key. Include all variables that affect the output.
const getUserPosts = unstable_cache(
async (userId: string) => db.posts.findMany({ where: { userId } }),
['user-posts'], // base key
{ tags: [`user-${userId}-posts`] } // per-user tag
);
// Call with userId — Next.js appends the argument to the key:
const posts = await getUserPosts(userId);Tags for On-Demand Revalidation
Tag cached functions and invalidate them with revalidateTag() when data changes — e.g., after a mutation in a Server Action.
// Server Action
async function createProduct(data: FormData) {
'use server';
await db.products.create({ data: parseFormData(data) });
revalidateTag('products'); // invalidates all cached functions tagged 'products'
redirect('/products');
}Time-Based Revalidation
Set revalidate: N in options to revalidate the cache after N seconds, similar to ISR for fetch calls.
const getStats = unstable_cache(
async () => computeHeavyStats(),
['dashboard-stats'],
{ revalidate: 300 } // recompute at most every 5 minutes
);Combining cache() and unstable_cache
Use cache() inside unstable_cache for functions called from multiple places in one render: dedup within request + persist across requests.
// Deduplicate the DB call within a request:
export const getRawUser = cache(async (id: string) => db.user.findUnique({ where: { id } }));
// And cache the result persistently:
export const getUser = unstable_cache(getRawUser, ['user'], { revalidate: 60, tags: ['user'] });Limitations of unstable_cache
unstable_cache is marked unstable because the API may change before stabilization. Cached functions cannot access request-specific data like cookies or headers.
React cache() for Layout Data
Wrap layout-level data fetches with cache() so both the layout and any nested page that calls the same function share the result within one render.
// lib/data.ts
export const getCurrentUser = cache(async () => {
const session = await getServerSession();
return db.user.findUnique({ where: { id: session?.userId } });
});
// layout.tsx and page.tsx both call getCurrentUser() — one DB queryWhen to Use Each
cache(): dedup identical calls within a render (same request). unstable_cache: persist results across multiple requests until revalidated. Use fetch + next.revalidate when calling external HTTP APIs.
Quick Check
What is the key difference between React's cache() and Next.js unstable_cache?
Recap
cache(fn) deduplicates calls within one render pass — ideal for shared layout/page data. unstable_cache(fn, keys, { revalidate, tags }) persists across requests with time-based or tag-based invalidation — ideal for expensive DB queries.
Frequently asked questions
Is the “unstable_cache & React cache()” lesson free?
Yes — the full text of “unstable_cache & React cache()” is free to read here on the web, and the React Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Academy course, upgrade to CoddyKit PRO.
What will I learn in “unstable_cache & React cache()”?
Cache expensive function results per-request or across requests with Next.js cache utilities. You practise React Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start React Academy?
No prior experience is required. React Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “unstable_cache & React cache()” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this React Academy lesson?
Yes. Every React Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- fetch() with Cache Options in Next.js
- unstable_cache & React cache()
- Incremental Static Regeneration (ISR)
- Server Actions for Data Mutations