Skip to the solution
HardFrontend6 min read

unstable_cache vs cache (React) — which dedupes what?

220 views

The problem

Question presented to candidate: "You have a getProductStats() function that runs an expensive SQL query. When would you wrap it in React's cache, when in unstable_cache, and what would you use in Next.js 16?"

What a strong answer should cover:

  • React cache() memoizes a function for one server request: duplicate calls in the same render run once, and the next request starts fresh.
  • unstable_cache from next/cache stores the result across requests (and deployments) with revalidate and tags, like the Data Cache for non-fetch functions.
  • They solve different problems and can be combined: persistence plus per-request dedupe.
  • In Next.js 16 the unstable_cache docs say it "has been replaced by use cache" (the 16.4 migration guide adds that it keeps working, so migrating is optional); with Cache Components you mark the function with 'use cache' and use cacheLife and cacheTag.
  • Never persist per-user results in a shared cache unless the user id is part of the key.

Clarifying questions expected:

  • "Must the result be fresh on every request?" — then only per-request dedupe applies.
  • "Is Cache Components enabled?" — decides between unstable_cache and 'use cache'.

Code / implementation expected: Yes — the same function wrapped both ways, and the Next.js 16 version.

  • caching
  • unstable_cache

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

6 min read · 8 sections

Read the code

Per-request dedupe, persistence, and the Next.js 16 form
import { cache } from "react";
import { unstable_cache } from "next/cache";
import { cacheLife, cacheTag } from "next/cache";

// Per request: several components may call it, the query runs once per request.
export const getCurrentUser = cache(async (id: string) => db.user.findUnique({ where: { id } }));

// Across requests (previous model): shared stats, refreshed hourly or by tag.
export const getProductStats = unstable_cache(
  async (productId: string) => db.$queryRaw`SELECT count(*) FROM orders WHERE product_id = ${productId}`,
  ["product-stats"],
  { revalidate: 3600, tags: ["stats"] },
);

// Next.js 16 with cacheComponents: true
export async function getProductStats16(productId: string) {
  "use cache";
  cacheLife("hours");
  cacheTag("stats");
  return db.$queryRaw`SELECT count(*) FROM orders WHERE product_id = ${productId}`;
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 121 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextHard

What is the Full Route Cache in Next.js?

Continue