EMZETT.
Login

React.cache()

In short: A React function that caches the result of an asynchronous call (e.g. a database query) within a single request — if you call the same function with the same arguments several times in the same request, the actual work only runs once.

In more detail: Important: the cache only applies for the duration of a single server request, not between different users or requests (no global/shared cache). Typical use case: a function like getCurrentUser() is often called in several independent places during one page view (e.g. once in the header, once in the page itself) — without React.cache() that means several identical database round trips per single page view.

Our context: This was the plausible main reason why Emzett’s Vercel server occasionally crashed, especially with several simultaneous visitors — getCurrentUser()/getSetting() ran twice on 11 of 12 pages, each with a full DB round trip. Wrapped in React.cache(), two calls became — verified live — a single real DB access.

In Depth

How it works: React.cache() remembers per request (technically: per React server render pass) which function has already been called with which arguments, and on the second identical call simply returns the already computed result instead of running the function again:

import { cache } from "react";
 
export const getCurrentUser = cache(async () => {
  const session = await getSession();
  if (!session) return null;
  return db.query.users.findFirst({ where: eq(users.id, session.userId) });
});

It’s important to distinguish it from other Next.js caching mechanisms that sound similar but do something else: fetch() caching and cache tags/revalidateTag cache data ACROSS pages and BETWEEN requests (until they are explicitly invalidated), whereas React.cache() explicitly deduplicates ONLY within the lifecycle of a single request and is then discarded — for the next user or the next page view the function runs completely normally again.

Why deduplication doesn’t work across different data

React.cache() recognises “identical calls” by comparing the arguments passed (similar to a memoisation key). If you call the same function with DIFFERENT arguments, deduplication doesn’t apply — each unique set of arguments runs once per request:

await getUserById("abc"); // actually runs
await getUserById("abc"); // from cache, no DB access
await getUserById("xyz"); // different arguments -> actually runs again

This is usually exactly the desired behaviour, but a common misconception: anyone who assumes React.cache() bundles EVERY database access in the request will be surprised when two slightly different calls (e.g. once with and once without an optional parameter) still trigger two real queries.

How it differs from useMemo

useMemo is client-side (runs in the browser, per component, across re-renders) and intended for synchronous computations — React.cache() is server-only, made for asynchronous data access, and deduplicates across the entire width of a request’s component tree, not just within a single component. The two solve different problems and can’t be swapped for each other.

See also: Neon, Neon Auto-Suspend, Cache tags/revalidateTag