Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
Next.jsReactJune 21, 20264 min read

Next.js Request Memoization: Stop Over-Fetching in Server Components

Next.js request memoization prevents redundant data fetching across nested components. Learn how to use the React cache function to boost performance.

Next.jsReactPerformanceWeb DevelopmentBackendFrontendTypeScript
Detailed view of a server rack with a focus on technology and data storage.

Last month, I was debugging a dashboard page that felt sluggish. After checking the logs, I realized we were hitting our internal user-service API four times for the exact same profile data during a single page render. It wasn't a complex state management issue; it was just a consequence of how our data-fetching logic was spread across a deeply nested tree of React Server Components.

When you're building with React Server Components vs Client Components in Next.js, it's easy to lose track of the request lifecycle. In a traditional client-side app, you might reach for React Query. But on the server, you need a strategy that understands the scope of a single request.

The Problem: The "Waterfall" of Duplicate Fetches

In Next.js, Server Components allow you to fetch data directly where you need it. This is great for developer experience—you don't need to prop-drill data from the top-level page.tsx down to a deeply nested UserAvatar component.

The downside? If Layout, Header, Sidebar, and ProfileCard all call getUser(), you've just triggered four network requests. Even if these are hitting a fast internal cache, the overhead adds up. I once saw a page load time drop from 450ms to about 180ms simply by eliminating three redundant database calls.

Using the cache Function for Next.js Performance Optimization

A well-organized set of metal tools in a green toolbox with ratchets and screwdrivers.

The most straightforward way to solve this is by using the React cache function. It’s designed specifically for this scenario: it memoizes the result of a function call based on its arguments, but only for the duration of a single request.

Here is how I typically implement this in a data.ts utility file:

TYPESCRIPT
import { cache } from CE9178">'react';

export const getUser = cache(async (id: string) => {
  const res = await fetch(CE9178">`https://api.myapp.com/users/${id}`);
  return res.json();
});

When you wrap your fetcher in cache, React ensures that within a single render pass, calling getUser('123') multiple times will only execute the underlying fetch once. The subsequent calls return the cached promise.

Why not just use a global variable?

I’ve seen engineers try to use a simple object or Map outside the function scope to cache results. Don't do this. Since Next.js runs in a long-lived Node.js process, a global cache will persist across different users' requests, leading to massive security holes and stale data. The cache function is request-scoped, making it the only safe choice for server-side memoization.

When Things Get Complicated: Deeply Nested Trees

Sometimes, you need to share data that isn't just a simple ID. If you're building a system that relies on Next.js AsyncLocalStorage: Type-Safe Request Context Injection to grab authenticated user info, you might find yourself needing to memoize based on the request context itself.

If you find your cache logic getting messy, consider these three rules:

  1. Keep it co-located: Define your cache functions as close to the data-fetching logic as possible. Don't create a giant api-cache.ts file that imports everything in your app.
  2. Arguments matter: The cache function uses the arguments as the key. If you pass an object, ensure it's stable, or you'll accidentally bypass the cache.
  3. Don't over-memoize: If a function isn't hitting the network or performing heavy computation, memoization adds unnecessary memory overhead.

The Trade-offs of Next.js Cache

While memoization is a powerful tool for performance optimization, it’s not a magic bullet. If you cache a function that performs a mutation or depends on highly volatile data, you might hide bugs where the UI doesn't reflect the true state of the database.

I once spent four hours tracking down a bug where a user profile update wasn't showing up because I had aggressively cached the getUser function. I had to implement a manual cache invalidation strategy using the revalidateTag or revalidatePath APIs, which added a layer of complexity I hadn't planned for.

Final Thoughts

Colorful confetti scattered over the word 'Finally' symbolizing celebration or achievement.

Memoization is essential when you're moving toward a component-driven architecture where every piece of the UI is responsible for its own data. It allows you to write clean, decoupled components without sacrificing speed.

However, be careful. Before you start wrapping every fetch in cache, verify that you actually have an over-fetching problem. Sometimes, the simplest solution is just to fetch the data at the page level and pass it down. It’s less "clever," but it’s much easier to debug when things go wrong.

Frequently Asked Questions

Does cache persist data across different users? No. The cache function is scoped to the lifecycle of a single request. It’s perfectly safe for user-specific data.

How does this interact with Next.js Data Cache? The React cache function handles memoization during a single render. The Next.js Data Cache (configured via fetch options like next: { revalidate: 60 }) handles caching across multiple requests. They serve two different purposes and work great together.

Can I use cache in Client Components? No. The cache function is a React feature intended for server-side execution. If you try to use it in a file with a "use client" directive, it will throw an error or fail silently.

Back to Blog

Similar Posts

Serene long exposure of a cascading waterfall surrounded by lush greenery in Shifen, Taiwan.
Next.jsReactJune 20, 20264 min read

Next.js App Router Data Fetching: Avoiding Performance Waterfalls

Learn how to master Next.js App Router data fetching by parallelizing server requests. Stop blocking your renders and fix performance waterfalls today.

Read more
Scrabble tiles spelling 'online store' on a rustic wooden background.
Next.jsReactJune 20, 20264 min read

Next.js Partial Prerendering: Optimizing Dynamic E-commerce Feeds

Next.js Partial Prerendering (PPR) lets you mix static and dynamic UI in one route. Learn how to optimize your e-commerce product feeds for instant loading.

Read more
Close-up of JavaScript code on a laptop screen, showcasing programming in progress.
ReactNext.jsJune 20, 20264 min read

Profiling and fixing a slow React render: A Practical Guide

Profiling and fixing a slow React render is easier when you stop guessing. Learn how to use React DevTools to find bottlenecks and optimize your app.

Read more