---
title: "Next.js 16.2 Caching Explained: unstable_cache vs use cache"
slug: "nextjs-16-2-caching-unstable-cache-vs-use-cache"
published: "2026-03-21"
updated: "2026-04-06"
validated: "2026-03-20"
categories:
  - "Next.js"
tags:
  - "Next.js caching"
  - "Next.js 16"
  - "unstable_cache"
  - "use cache"
  - "Cache Components"
  - "revalidateTag"
  - "cacheTag"
  - "cacheLife"
  - "Payload CMS"
  - "server-side caching"
  - "App Router"
llm-intent: "reference"
audience-level: "intermediate"
framework-versions:
  - "nextjs@16.1+"
  - "typescript@5.x (recommended)"
status: "stable"
llm-purpose: "Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…"
llm-prereqs:
  - "Access to Next.js 16"
  - "Access to TypeScript"
  - "Access to Payload CMS"
  - "Access to next/cache"
  - "Access to App Router"
llm-outputs:
  - "Completed outcome: Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…"
---

**Summary Triples**
- (unstable_cache, is, a server-side data-layer caching function that wraps async computations (DB/CMS fetches) in Next.js 16)
- (Cache Components, are, an application-level rendering and caching model that controls component render caching and lifecycle)
- (use cache, is, a React directive that provides component-level caching but only makes sense when Cache Components are enabled)
- (unstable_cache, should_be_used_when, Cache Components are not enabled or you need data-layer caching for server fetches (e.g., Payload CMS responses))
- (use cache, should_be_used_when, Cache Components are enabled and you want to shift caching responsibilities into the component render layer)
- (migrating_from_unstable_cache_to_use_cache, requires, enabling Cache Components and refactoring data-fetch/caching responsibilities from helper functions into cached components)
- (next/cache features, include, tag-based invalidation and TTL/revalidation controls (revalidateTag/cacheTag/cacheLife patterns))
- (Payload CMS integrations, recommendation, use unstable_cache on the server for predictable, data-layer caching unless you enable Cache Components and intentionally refactor)

### {GOAL}
Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…

### {PREREQS}
- Access to Next.js 16
- Access to TypeScript
- Access to Payload CMS
- Access to next/cache
- Access to App Router

### {STEPS}
1. Identify caching layers
2. Evaluate project readiness
3. Use unstable_cache for isolated data
4. Enable Cache Components for new apps
5. Adopt use cache with tags and life
6. Plan a phased migration
7. Test revalidation and monitor

<!-- llm:goal="Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…" -->
<!-- llm:prereq="Access to Next.js 16" -->
<!-- llm:prereq="Access to TypeScript" -->
<!-- llm:prereq="Access to Payload CMS" -->
<!-- llm:prereq="Access to next/cache" -->
<!-- llm:prereq="Access to App Router" -->
<!-- llm:output="Completed outcome: Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…" -->

# Next.js 16.2 Caching Explained: unstable_cache vs use cache
> Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16.2 to pick the right approach for integrations and migrations.
Matija Žiberna · 2026-03-21

I was integrating Payload CMS into an existing Next.js app recently. The app was already running on Next.js 16.2+, which should mean I have access to all the modern caching tooling. I open the docs, see `unstable_cache` marked as legacy, read that `use cache` is the new recommended directive, and then realize that Cache Components are not actually enabled in this project.

So what am I supposed to use?

If you have hit that same wall, this article is for you. The confusion is not a reading comprehension problem. It comes from three genuinely separate things being discussed as though they are one caching story. Once you understand what each piece actually is, the decision becomes straightforward.

---

## Why Next.js 16.2 Caching Feels Confusing

The short answer is that developers are conflating three distinct concepts:

- `unstable_cache`, which is a data-layer caching API
- Cache Components, which is an application-level rendering and caching model
- `use cache`, which is a new directive that only makes sense once Cache Components are enabled

Documentation and community discussion often skip over the distinction between the second and third point. People see `unstable_cache` labelled legacy, read that `use cache` is the replacement, and assume they can swap one for the other. But `use cache` does not live in the same layer as `unstable_cache`. They solve overlapping problems but are not drop-in replacements for each other without a broader architectural change.

Let us go through each piece properly.

---

## What unstable_cache Actually Is

`unstable_cache` is a server-side function that caches the result of an async computation. You wrap a database query, a CMS fetch, or any expensive server-side call, and Next.js caches the return value across requests.

```typescript
// File: app/lib/get-posts.ts
import { unstable_cache } from "next/cache";
import { getPayload } from "payload";
import config from "@payload-config";

export const getCachedPosts = unstable_cache(
  async () => {
    const payload = await getPayload({ config });
    const posts = await payload.find({
      collection: "posts",
      limit: 20,
    });
    return posts.docs;
  },
  ["posts-list"],
  {
    revalidate: 3600,
    tags: ["posts"],
  }
);
```

The function takes three arguments: the async function to cache, a key array that identifies this cache entry, and an options object where you set `revalidate` for time-based expiry or `tags` for on-demand revalidation via `revalidateTag`.

What this does is straightforward. The first call executes the function and stores the result. Subsequent calls within the revalidation window return the cached result without hitting the database or CMS again. You can also call `revalidateTag("posts")` from a webhook or route handler to invalidate the cache on demand.

The "unstable" prefix has been there since it was introduced. It does not mean broken — it means the API signature was subject to change. In current docs it is labelled legacy, but it still works exactly as it always has and it is present in a large number of production Next.js projects.

---

## What Cache Components Actually Are

Cache Components are not a function or a directive. They are an application-level feature that changes how the App Router handles rendering and caching by default.

When Cache Components are enabled, the rendering model shifts. Request-time data is no longer cached unless you explicitly opt in. Components and functions can declare their own caching behavior using `use cache`. The framework treats caching as something you opt into deliberately, rather than something that happens automatically at the fetch or route level.

Enabling Cache Components requires opting in via your Next.js config:

```typescript
// File: next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  experimental: {
    cacheComponents: true,
  },
};

export default nextConfig;
```

This is an experimental flag and it changes the behavior of the entire application. It is not a feature you enable in isolation for one route or one component. Enabling it on an existing app means you need to review how caching works across the whole project.

That context matters. In a greenfield project, you would likely enable this from the start. In an existing production app with established caching patterns, enabling it mid-project is a decision that needs careful planning.

---

## What use cache Actually Is

`use cache` is a directive, similar in concept to `"use client"` or `"use server"`. You add it at the top of a file, a component, or a function, and it tells Next.js to cache that piece of work.

```typescript
// File: app/components/PostList.tsx
"use cache";

import { getPayload } from "payload";
import config from "@payload-config";

export async function PostList() {
  const payload = await getPayload({ config });
  const posts = await payload.find({
    collection: "posts",
    limit: 20,
  });

  return (
    <ul>
      {posts.docs.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
```

You can also use it at the function level rather than the file level, which gives you more granular control:

```typescript
// File: app/lib/get-posts.ts
import { unstable_cacheTag as cacheTag, unstable_cacheLife as cacheLife } from "next/cache";

async function getCachedPosts() {
  "use cache";
  cacheTag("posts");
  cacheLife("hours");

  const payload = await getPayload({ config });
  const posts = await payload.find({
    collection: "posts",
    limit: 20,
  });

  return posts.docs;
}
```

Notice `cacheTag` and `cacheLife`. These are the companion APIs for `use cache`. `cacheTag` applies a revalidation tag the same way the `tags` option does in `unstable_cache`. `cacheLife` lets you reference named cache profiles instead of raw seconds, which reads more cleanly for shared codebases.

`use cache` is the intended path forward for Next.js caching. It is more declarative, composable, and consistent with the directive model the framework already uses. But it is designed to work within a Cache Components setup. Without that flag enabled, its behavior is either unavailable or unreliable depending on the Next.js version.

---

## Why These Are Not the Same Thing

This is where the confusion collapses into something clear.

`unstable_cache` is an API. You call it, you wrap a function, you get back a cached version of that function. It works at the data layer and does not depend on how the rest of your app is configured.

Cache Components is the rendering and caching model. It is a project-level architectural choice. Enabling it changes what caching means across the entire application.

`use cache` is the directive that lives inside the Cache Components model. It declares caching intent at the file, component, or function level. It is the developer-facing API for that model.

The relationship is: Cache Components is the foundation, `use cache` is the tool you use on top of it. `unstable_cache` is a separate, older tool that operates outside that model.

Switching from `unstable_cache` to `use cache` is not just swapping a function call. It is adopting a different caching architecture. In a new project, that is the obvious right path. In an existing project, it is a deliberate migration decision with real scope.

---

## Real-World Case: Payload CMS in an Existing Next.js 16 App

Here is the scenario I was actually working with.

An existing Next.js application, already running on 16.1+. An established codebase with existing caching patterns. Cache Components not enabled. I was brought in to integrate Payload CMS as the content layer — an isolated concern, scoped carefully to avoid disrupting what was already working.

In that context, enabling Cache Components was not on the table. The host development team had not planned for it, had not reviewed the caching implications app-wide, and had no immediate reason to take on that migration as part of a CMS integration project.

The right tool for that situation was `unstable_cache`.

```typescript
// File: app/lib/payload-cache.ts
import { unstable_cache } from "next/cache";
import { getPayload } from "payload";
import config from "@payload-config";

export const getCachedPage = unstable_cache(
  async (slug: string) => {
    const payload = await getPayload({ config });
    const result = await payload.find({
      collection: "pages",
      where: { slug: { equals: slug } },
      limit: 1,
    });
    return result.docs[0] ?? null;
  },
  ["page-by-slug"],
  {
    revalidate: 3600,
    tags: ["pages"],
  }
);
```

This is clean, scoped, and does not touch anything else in the application. The Payload integration gets its own caching layer. The rest of the app continues working exactly as before. On-demand revalidation via `revalidateTag("pages")` hooks into a Payload webhook cleanly.

Using `unstable_cache` here was not ignorance of the newer API. It was a deliberate compatibility choice. That distinction matters.

---

## When unstable_cache Is Still the Right Choice

There are real situations where `unstable_cache` is the appropriate tool, even in a project running on Next.js 16.

If you are working in an existing production application where Cache Components are not enabled, using `use cache` without enabling that flag is not a supported pattern. The "legacy" label in the docs is a forward-looking signal, not a deprecation notice for running code.

If you are scoping an isolated piece of work — a CMS integration, a new data layer, an added feature — and the rest of the codebase has no immediate plans to adopt the newer caching model, the pragmatic choice is the one that introduces the least disruption.

If you are building something that will eventually migrate to `use cache`, `unstable_cache` does not create a structural mess. The pattern is similar enough that migration is straightforward once the team is ready.

The framework continues to support it. The mental model carries forward. Use it confidently where it fits.

---

## When to Migrate to use cache

The right moment to migrate is when the application as a whole is ready to adopt Cache Components.

That means the team has decided to enable the experimental flag, has audited how existing caching behavior changes under the new model, and has the capacity to update caching declarations across the affected parts of the app.

At that point, `use cache` is the direction to move toward. It is more composable, reads more clearly in component-heavy codebases, and aligns with where the framework is heading. `cacheTag` and `cacheLife` are cleaner than managing raw key arrays and numeric revalidation values. The directive model fits naturally alongside `"use client"` and `"use server"` in the App Router pattern.

If you are starting a new project today with full control over the config from the beginning, enable Cache Components and use `use cache` from the start. That is the intended path.

---

## The Practical Decision Tree

When you hit a caching decision in Next.js 16, work through this in order.

Does this data need to be fresh on every request? Do not cache it. Fetch it directly inside your component or server action.

Are you caching HTTP responses via `fetch`? Use the `fetch` cache options — `next: { revalidate: 3600 }` or `next: { tags: ["tag"] }`. That layer works independently of everything else.

Are you caching a database query, a CMS call, or another server-side computation? Check whether Cache Components are enabled in the project.

If Cache Components are enabled, use `use cache` with `cacheTag` and `cacheLife`. That is the intended modern path.

If Cache Components are not enabled, use `unstable_cache`. It is the right tool for that context, regardless of what the docs call it.

---

## Conclusion

The best caching choice is not always the newest API. It is the one that fits the current project architecture without creating unnecessary migration risk.

`unstable_cache`, Cache Components, and `use cache` are three things that belong to overlapping but distinct layers. Seeing one marked legacy does not mean your existing approach is wrong. It means the framework has a preferred direction for new projects and for teams ready to adopt it.

Know which layer you are working in. Know whether Cache Components are enabled. Make the call that fits the project, not the one that looks most modern in isolation.

Let me know in the comments if you have questions, and subscribe for more practical development guides.

Thanks,
Matija

## LLM Response Snippet
```json
{
  "goal": "Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…",
  "responses": [
    {
      "question": "What does the article \"Next.js Caching Explained: unstable_cache vs use cache\" cover?",
      "answer": "Next.js caching: compare unstable_cache, Cache Components, and 'use cache' in Next.js 16 to pick the right approach for integrations and migrations. Read…"
    }
  ]
}
```