Request Memoization
In a typical page, a layout, the page itself, and several nested components might all need the same piece of data — the current user, for example. Without any special handling, that could mean the same fetch call runs several times over during a single render. React and Next.js solve this with request memoization: identical fetch calls made during the same render pass are automatically deduplicated.
The problem it solves
Imagine a layout and a page that each independently need the logged-in user's profile, without either one passing it down as a prop.
// app/[locale]/dashboard/layout.tsx
async function getUser() {
const res = await fetch('https://api.example.com/me')
return res.json()
}
export default async function DashboardLayout({ children }: { children: React.ReactNode }) {
const user = await getUser()
return (
<div>
<p>Welcome, {user.name}</p>
{children}
</div>
)
}// app/[locale]/dashboard/page.tsx
async function getUser() {
const res = await fetch('https://api.example.com/me')
return res.json()
}
export default async function DashboardPage() {
const user = await getUser()
return <h1>{user.name}'s Dashboard</h1>
}Both getUser functions call fetch with the exact same URL and options. Naively, that would be two separate network round trips for identical data during the render of a single page. Instead, React memoizes the result: the layout's call actually performs the fetch, and the page's call — being identical in URL and options — receives the same cached Promise without triggering a second network request.
Scoped to one request, not persisted across requests
Request memoization only lasts for the duration of a single server render — one incoming request. It is not the same as the Data Cache, which can persist across many different requests and even across deployments. Once the response has been sent, the memoization cache for that render is discarded.
Mechanism | Scope | Applies to |
|---|---|---|
Request Memoization | A single render pass (one request) | fetch() calls with identical URL + options |
Data Cache | Across requests, and across deployments by default | fetch() calls, based on cache/revalidate/tags options |
Only fetch is memoized automatically
import { cache } from 'react'
import { db } from '@libs/db'
// Wrapping a non-fetch data access function with cache() gives it
// the same per-request deduplication that fetch() gets automatically.
export const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } })
})Why this matters
You can call the same data-fetching function from a layout, a page, and any nested Server Component without worrying about duplicate network requests.
It encourages colocating data requirements with the components that use them, instead of fetching once at the top and drilling props down through many layers.
It only works for calls with matching URL and options — even a differently ordered header or a different query parameter creates a separate, non-deduplicated entry.