Next.js Request Deduplication vs React `cache()`: How They Differ

Next.js deduplicates fetch automatically, and React cache deduplicates any async function you wrap. Learn what each covers and when to use which.

7 min read

Next.js automatically deduplicates fetch requests, and React provides a cache function that deduplicates any async work you wrap. They produce the same visible result but cover different ground: fetch deduplication is automatic and narrow, while React cache is manual and general.

fetch deduplicationReact cache()
AutomaticYesNo, you wrap the function
Coversfetch GET with the same URL and optionsAny async function
LifetimeOne render passOne server request
In route handlersNoNo, the cache is only readable inside the component tree

How fetch deduplication works

When a layout and a page call the same fetch, Next.js sends one network request and shares the response between them for that render.

typescripttypescript
// lib/posts.ts
export async function getPosts() {
  const res = await fetch("https://api.example.com/posts");
  return res.json();
}

This helper fetches the posts once and returns the parsed body. Because it wraps fetch, Next.js memoizes it automatically inside a single render.

App.tsxApp.tsx
// app/posts/layout.tsx
import { getPosts } from "@/lib/posts";
 
export default async function PostsLayout({ children }: LayoutProps<"/posts">) {
  const posts = await getPosts();
  return <main><p>{posts.length} posts</p>{children}</main>;
}

The layout needs the post count for its header, so it calls getPosts itself instead of receiving the data from the page below it.

App.tsxApp.tsx
// app/posts/page.tsx
import { getPosts } from "@/lib/posts";
 
export default async function PostsPage() {
  const posts = await getPosts();
  return <ul>{posts.map((post) => <li key={post.id}>{post.title}</li>)}</ul>;
}

Both files call getPosts, but the network is hit once. Pass an AbortSignal to opt out when you want a fresh call each time. This memoization lasts for a single render pass, not across requests.

How React cache() works

React cache turns an async function into a memoized version for Server Components. It deduplicates by argument and invalidates on every server request, so two components that call it with the same input run the work once.

typescripttypescript
// lib/user.ts
import { cache } from "react";
import { db } from "@/lib/db";
 
export const getUser = cache(async (id: string) => {
  return db.user.findUnique({ where: { id } });
});

A database query is exactly the case fetch deduplication cannot help with, because no fetch is involved. The memoized function is defined once in a shared module, which is what lets every component read from the same memo.

Now a layout header and a page body can both call getUser with the same id and the query runs once for that request. The second call returns the stored promise instead of touching the database, and the memo is thrown away when the request ends. The same wrapper works for any async helper, as shown in fetching data directly from a database.

Which one to use

If the data comes from fetch, do nothing: it is already deduplicated. If the data comes from an ORM, a database, or a custom async function, wrap it in cache so repeated calls within a request run once.

When the work is already covered by fetch deduplication, adding cache on top adds nothing. The two mechanisms overlap, but they are not interchangeable.

Common mistakes

  • Calling a memoized function outside a component does not use the cache, because React only provides cache access inside the component tree.
  • Defining cache inside a component creates a new memoized function every render, so no two calls ever share.
  • Assuming fetch deduplication applies in route handlers. It does not, because route handlers are outside the component tree.

For how these fetch requests interact with the persistent framework cache, see using fetch in Next.js.

Rune AI

Rune AI

Key Insights

  • fetch deduplication is automatic and covers fetch calls only.
  • React cache wraps any async function and is opt-in.
  • fetch memoization lasts one render, while cache lasts one server request.
  • Neither applies outside the component tree.
  • Use cache for ORM and database calls that fetch cannot see.
RunePowered by Rune AI

Frequently Asked Questions

Are fetch deduplication and React cache the same thing?

No. fetch deduplication is automatic and covers fetch calls only. React cache is an opt-in wrapper for any async function, such as a database query.

Does fetch deduplication work in route handlers?

No. Memoization only applies inside the React component tree, so a route handler fetching twice will issue two requests.

When do I need React cache?

When your data comes from an ORM or database instead of fetch, and two components would otherwise run the same query twice.

Conclusion

Next.js deduplicates fetch automatically within a render, while React cache is a manual wrapper for any async function. Use fetch's built-in behavior for HTTP, and wrap database or ORM calls in cache to get the same single-request guarantee.