Skip to Content

Next.js — Cache Components & Caching Guide

Overview

Cache Components in Next.js is a modern approach to rendering and caching that gives fine-grained control over what is cached, when, and how it’s reused. It leverages Partial Prerendering (PPR) to improve performance and user experience.

Server components that use async functions from Next.js, such as cookies() or headers(), can introduce load time. To handle this efficiently, Next.js allows caching server component results and reusing them across renders.


Rendering Async Server Components with Suspense

When rendering an async server component inside a client component, you must wrap it in <Suspense>. Without Suspense, React will throw an error because async server components are not directly renderable on the client.

// Async Server Component async function AsyncServerComponent() { const res = await fetch('https://api.example.com/data'); const data = await res.json(); return <div>{data.title}</div>; } // Client Component import { Suspense } from 'react'; export default function ClientWrapper() { return ( <Suspense fallback={<div>Loading...</div>}> <AsyncServerComponent /> </Suspense> ); }

The fallback UI can be any React element (spinner, skeleton loader, etc.), providing a seamless user experience.


How to Cache Components

1. Using cache from Next.js

Next.js allows server component caching to reuse component results without re-fetching data on every render.

import { cache } from 'react'; const fetchCachedData = cache(async () => { const res = await fetch('https://api.example.com/data', { next: { revalidate: 60 } }); return res.json(); }); export default async function MyServerComponent() { const data = await fetchCachedData(); return <div>{data.title}</div>; }

Where & Why:

  • Where: Wrap expensive async functions in cache().
  • Why: Prevents repeated fetching and computation, reducing server load and improving render speed.

2. Caching Fetch Results

Next.js fetch API supports caching via options:

const res = await fetch('https://api.example.com/data', { next: { revalidate: 60, // cache for 60 seconds tags: ['products'], // optional cache tagging }, });

How it works:

  • cache() caches the server component result.
  • revalidate defines how long the cache is fresh.
  • tags allow selective invalidation when a resource changes.

TanStack Query Comparison:

  • TanStack Query also caches fetch results on the client.
  • Both approaches aim to avoid repeated fetching.
  • Next.js caching happens server-side, while TanStack Query works client-side.

Cache Tags Explained

Tag / OptionDescriptionWhen to Use
cacheTagTags that identify cached data. Can be invalidated selectively.When you want to invalidate specific components or fetches.
revalidateTagAutomatically refreshes a cached component when the tag changes.Use for dynamic data that updates often.
updateTagManually triggers an update for a cached resource.When backend data changes and you need immediate UI update.
cacheLifeHow long the cached data remains valid.Short-lived: dynamic content; long-lived: static content.

Use tags and cache lifetimes judiciously to balance freshness and performance.


  • <Link> is used for client-side navigation between pages.
  • Do NOT rely on Link to trigger cache invalidation. It does not affect server component caches or tags.
  • Use cacheTag, revalidateTag, or updateTag to control caching behavior explicitly.

How Next.js Fetches Pages

  • Pages in Next.js are prefetched in the background when using <Link> (client-side navigation).

  • When a page fetches data:

    • It uses server-side rendering (SSR) or static generation (SSG) depending on your setup.
    • Cached components or fetches reduce repeated server calls.
  • Recommended workflow:

    1. Wrap expensive async server components in cache().
    2. Use fetch with revalidate and tags options.
    3. Use <Suspense> when rendering async server components in client components.
    4. Use cache tags to selectively invalidate data when necessary.

References

For the most up-to-date and detailed information, please refer to the official documentation:


Summary

  • Server components with async operations can block rendering; wrap them in <Suspense> with a fallback.
  • cache() helps avoid recomputing expensive components.
  • Fetch caching reduces network requests and improves performance.
  • Tags (cacheTag, revalidateTag, updateTag) allow fine-grained cache control.
  • Client-side <Link> navigation does not affect server-side caches.
  • Combine Suspense + caching + fetch options for optimal page performance.