Skip to the solution
HardFrontend12 min read

What are Cache Components in Next.js 16 and how does the 'use cache' directive work?

815 views

The problem

Question presented to candidate: "Our product page has a marketing header that almost never changes, a price that changes a few times a day, and a greeting that depends on the visitor's cookie. In older Next.js the whole route ended up either static or dynamic. How do Cache Components and the 'use cache' directive let one page mix all three?"

What a strong answer should cover:

  • cacheComponents: true in next.config.ts turns the model on: data access is dynamic by default and you opt in to caching. In 16.3+ it is set together with partialPrefetching, and 16.4 makes it the create-next-app default.
  • 'use cache' marks an async function, component or whole file as cacheable; the cache key is built from the build ID, a function ID and the serialized arguments (including captured closure variables).
  • cacheLife sets the lifetime (stale, revalidate, expire) and cacheTag attaches tags for on-demand invalidation; without cacheLife the default profile applies.
  • Partial Prerendering is the result: cached and static output goes into a prerendered static shell, while runtime data (cookies(), headers(), uncached fetches) streams in behind <Suspense>.
  • Constraints: no cookies()/headers() inside a cached scope (read outside and pass as arguments), arguments must be serializable, and uncached data outside <Suspense> fails the build.
  • The old flags are gone: experimental.ppr, experimental_ppr and experimental.dynamicIO now produce errors that point to cacheComponents.

Clarifying questions expected:

  • "Is cacheComponents already enabled, or are we migrating a route-segment-config app?" β€” the migration path differs.
  • "How stale may the price be, and who triggers updates?" β€” that decides the cacheLife profile and whether tags are needed.
  • "Where do we host?" β€” the default cache is in memory per instance, which matters for serverless.

Code / implementation expected: Yes β€” a page with a cached component, a runtime component in <Suspense>, and the config flag.

  • nextjs
  • caching
  • rsc
  • next-16

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

12 min read Β· 8 sections

Read the code

Page mixing static, cached and per-request content
import { Suspense } from "react";
import { cookies } from "next/headers";
import { connection } from "next/server";
import { cacheLife, cacheTag } from "next/cache";

async function CachedClock() {
  "use cache";
  cacheLife("hours");
  cacheTag("clock");
  return <p id="cached">cached at {new Date().toISOString()}</p>;
}

async function Visitor() {
  const theme = (await cookies()).get("theme")?.value ?? "none";
  await connection(); // the timestamp below must come from a real request
  return <p id="live">theme={theme} rendered at {new Date().toISOString()}</p>;
}

export default function Page() {
  return (
    <main>
      <h1>Product page</h1>
      <CachedClock />
      <Suspense fallback={<p>loading visitor...</p>}>
        <Visitor />
      </Suspense>
    </main>
  );
}
A cached function keyed by its argument, used from a Route Handler
// lib/data.ts
import { cacheLife } from "next/cache";

export async function getPrice(sku: string) {
  "use cache";
  cacheLife("minutes");
  await new Promise((r) => setTimeout(r, 300));
  return { sku, price: sku.length * 10, computedAt: new Date().toISOString() };
}

// app/api/key/route.ts
import { getPrice } from "@/lib/data";

export async function GET(request: Request) {
  const sku = new URL(request.url).searchParams.get("sku") ?? "none";
  const t0 = Date.now();
  const data = await getPrice(sku);
  return Response.json({ ...data, tookMs: Date.now() - t0 });
}
next.config.ts for Cache Components (16.3+)
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true, // set explicitly; leaving it unset logs a warning
};

export default nextConfig;

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 107 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 Partial Prerendering (PPR) in Next.js?

Continue