f in x
> cd .. / HUB_EDITORIALE
Sviluppo di siti web

Data Fetching in Next.js — Cache, Revalidation and ISR for Applications That Don't Slow Down

[2026-06-23] Author: Ing. Calogero Bono

Your Next.js site is fast in development, but in production every page takes seconds because data is fetched from scratch on each request. Or you have data that changes every hour and you don't know whether to go static or dynamic, risking stale content or poor performance. We know this scenario well: we see it in many projects that come to us, where caching is neglected and Time to First Byte (TTFB) rises for no reason.

Next.js provides a multi-level caching system (Data Cache, Full Route Cache, Router Cache) that, when used correctly, delivers blazing-fast pages without sacrificing fresh data. In this guide we cover how to control fetch caching, how to use time-based and on-demand revalidation, and how to leverage Incremental Static Regeneration (ISR) to combine static performance with automatic updates. No academic theory: only what you need to make the right choice for your project.

How does the default data fetching caching work in Next.js?

To know what to change, you first need to understand what Next.js does by default when you use fetch inside a Server Component. Without specifying options, Next.js adopts the static behavior: fetch results are stored in the persistent Data Cache on disk, and the generated page is cached in the Full Route Cache (static). This means that at build time, the page is generated once and served to all users without re-running the fetch. Perfect for rarely-changing data (e.g. “About Us” page, blog posts).

But beware: if your data changes frequently and you don't manage caching, you'll serve outdated content. We've seen clients with product catalogs stuck on old data for days because they hadn't configured revalidation.

Sponsored Protocol

The three caching layers: what really matters

You don't need to memorize every detail, but it helps to know:

  • Data Cache: where fetch results are stored. Persistent and shared across requests.
  • Full Route Cache: the complete HTML page generated by the Server Component. If active, the response is instant.
  • Router Cache: in the browser, for client-side navigation.

When we talk about data fetching caching, we mainly intervene on the Data Cache and Full Route Cache via fetch options and Next.js APIs.

How and when to use time-based revalidation with fetch?

Time-based revalidation (or periodic revalidation) is the simplest method to keep data fresh while keeping the benefits of caching. You add the option next: { revalidate: seconds } to your fetch call.

Example: fetching a list of posts updated every 60 seconds.

// app/posts/page.tsx
export default async function PostsPage() {
  const res = await fetch('https://api.example.com/posts', {
    next: { revalidate: 60 }
  });
  const posts = await res.json();

  return (
    
    {posts.map(post =>
  • {post.title}
  • )}
); }

What happens exactly?

  1. On the first request after build, Next.js executes the fetch, stores the result in the Data Cache, and returns the page.
  2. For the next 60 seconds, every request receives the cached page (without re-running the fetch).
  3. After 60 seconds, the next request triggers a revalidation in the background: Next.js still serves the old page but starts a new fetch. If the fetch succeeds, the cache is updated for subsequent requests; if it fails, the old page remains valid.

This mechanism is ideal for data that changes at a predictable frequency (e.g. news feed, prices updated every hour). We use it often in WooCommerce projects to sync the catalog without overloading the server on every visit.

Sponsored Protocol

On-demand revalidation with revalidateTag and revalidatePath

When data changes at unpredictable times (e.g. a user edits a post, a new order arrives), time-based revalidation is not enough: you risk serving stale data until the next interval. Next.js offers two server-side functions (server actions, webhook, API routes):

  • revalidatePath('/path') — invalidates the cache for a specific route (or all routes containing that path).
  • revalidateTag('tag') — invalidates all fetches that have that tag, regardless of the route.

To use tags, add them to your fetch:

// during fetch
const res = await fetch('https://api.example.com/products', {
  next: { tags: ['products'] }
});

// after an update (e.g. in a server action)
import { revalidateTag } from 'next/cache';

export async function updateProduct(id: string, data: any) {
  // ... update database
  revalidateTag('products');
}

When to prefer tags? If multiple pages display the same data (e.g. product list, product detail, related products), a single tag invalidates all relevant caches. revalidatePath is more granular and useful for invalidating a single page (e.g. the homepage after publishing a new article).

How does ISR (Incremental Static Regeneration) work in Next.js?

ISR is a technique that combines static site generation (SSG) with the ability to update pages after build, without rebuilding the entire site. In Next.js App Router, ISR is achieved by combining generateStaticParams with the revalidate option in the fetch or at the page level (export const revalidate = 60).

Sponsored Protocol

Imagine a blog with hundreds of articles. With ISR you can statically generate the most visited pages at build time, and then progressively update them as new or modified pages are requested.

// app/blog/[slug]/page.tsx

export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts').then(r => r.json());
  return posts.map(post => ({ slug: post.slug }));
}

export default async function BlogPost({ params }: { params: { slug: string } }) {
  const res = await fetch(`https://api.example.com/posts/${params.slug}`, {
    next: { revalidate: 300 } // 5 minutes
  });
  const post = await res.json();

  return 
{post.content}
; }

How ISR works step by step:

  1. During next build, only pages listed in generateStaticParams are statically generated.
  2. On the first request for a non-pre-generated page (slug not in the parameter list), Next.js generates it dynamically and caches it in the Full Route Cache. This is the lazy generation typical of ISR.
  3. Each cached page has a TTL defined by revalidate. After that time, the next request triggers a background revalidation (as with periodic revalidation).
  4. If the fetch fails during revalidation, the old page remains served (stale-while-revalidate).

Don't confuse ISR with fetch-level revalidation: in this example, revalidate: 300 on the fetch causes the Data Cache to expire every 5 minutes. The page (Full Route Cache) follows the same TTL, but may be regenerated sooner if the Data Cache is already invalidated. In practice, for ISR, setting revalidate at the fetch level is enough; Next.js takes care of regenerating the page when needed.

Sponsored Protocol

ISR with revalidatePath for immediate updates

Periodic ISR is not enough when content is modified by a user action (e.g. an editor publishes a new post). In these cases, combine it with revalidatePath or revalidateTag after saving. Typical example: a webhook from a headless CMS:

// app/api/revalidate/route.ts
import { revalidatePath } from 'next/cache';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const secret = request.headers.get('x-secret');
  if (secret !== process.env.REVALIDATION_SECRET) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }
  
  const { slug } = await request.json();
  revalidatePath(`/blog/${slug}`);
  return NextResponse.json({ revalidated: true });
}

With this approach, as soon as the CMS notifies an update, Next.js invalidates the cache for that specific page. No waiting, no full rebuild.

Which caching strategy should you choose for dynamic and static data?

There is no single answer. At Meteora Web, we recommend starting from this decision matrix:

  • Data that changes less than once a day: use the default caching (no revalidate) or next: { revalidate: 86400 }. You get static pages at build time, very fast. For updates, use a webhook to revalidate.
  • Data that changes every few minutes (e.g. prices, availability): time-based revalidation with revalidate: 60 or 300. Accept a slight delay for non-critical data.
  • Real-time data (chat, notifications, dashboards): avoid server Data Cache. Use fetch(url, { cache: 'no-store' }) or fetch(url, { next: { revalidate: 0 } }). Pages will be dynamic and always fresh, but you lose server-side caching benefits.
  • Pages with lots of static content and some dynamic parts: use Streaming with Suspense and React Server Components. Static parts are cached, dynamic parts are fetched without blocking page rendering. Next.js supports this natively with loading.js and streaming.

A common mistake we fix in inherited projects: using cache: 'no-store' on every fetch for fear of stale data, losing all caching benefits. If you have only a few critical data points, isolate them in separate components and leave the rest static.

Sponsored Protocol

What to do now

  1. Analyze the update frequency of your application's data. Divide pages into three categories: static, semi-dynamic, dynamic.
  2. For each fetch, choose a strategy: revalidate: n for regularly changing data, tags + on-demand revalidation for data modified by user actions, cache: 'no-store' only where needed.
  3. Implement at least one revalidation endpoint (webhook or server action) to update the cache without a full rebuild.
  4. Verify with next build that pages are statically generated as expected, and use next dev with NODE_ENV=production to test caching.
  5. Monitor TTFB on cached vs non-cached pages. If you see times above 200ms on static pages, something is wrong (probably a non-cached fetch).

To dive deeper into the Next.js ecosystem, read our main guide: Next.js App Router — Server Components, Data Fetching and Full-Stack for Applications That Deliver.

If you have questions about specific cases (e-commerce, dashboards, blogs with millions of pages), contact us. Every day we work on these aspects with Italian companies that want real performance, not just nice words.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()