How to Properly Revalidate Next.js Cache (App Router & Pages Router)

Next.js’s caching and revalidation system has evolved significantly with the App Router (Next.js 13/14). While the Pages Router relied on getStaticProps and revalidate, the App Router introduces a new, powerful caching model centered around the fetch API, server actions, and on-demand cache invalidation functions.

This guide explains how to properly revalidate cache in both the App Router and the legacy Pages Router, covering time-based and on-demand approaches.


Understanding Cache Revalidation in Next.js

Revalidation ensures your statically rendered content stays fresh while maintaining fast load times. In Next.js, it works by regenerating data or pages when specific conditions are met.

In Summary:

  • Pages Router: Uses getStaticProps with revalidate or res.revalidate().
  • App Router: Uses fetch(..., { next: { revalidate } }), revalidatePath(), and revalidateTag().

App Router Cache Revalidation (Next.js 13/14)

The App Router introduced a new caching architecture with three layers:

  1. Request Memoization – Prevents duplicate fetch calls within the same request lifecycle.
  2. Data Cache – Stores fetched data responses across requests.
  3. Full Route Cache – Caches the rendered output of routes (HTML + React tree).

These layers work together to provide a flexible, high-performance caching model.


1. Time-Based Revalidation in App Router

Time-based revalidation in the App Router is configured directly inside the fetch() call.

Example:

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

  const posts = await res.json();

  return (
    <div>
      <h1>Latest Posts</h1>
      <ul>
        {posts.map(post => <li key={post.id}>{post.title}</li>)}
      </ul>
    </div>
  );
}

How it works:

  • The page is statically rendered and cached.
  • After 60 seconds, the next request triggers a background revalidation.
  • The updated data replaces the old cache seamlessly.

Note: There’s no getStaticProps or getServerSideProps in the App Router—all data fetching happens via fetch() or server actions.


2. On-Demand Revalidation in App Router

Next.js now offers two new functions for manual cache revalidation:

  • revalidatePath(path) — Revalidates a specific route path.
  • revalidateTag(tag) — Revalidates all fetches associated with a tag.

You can call these from Route Handlers or Server Actions.

Example: Using revalidatePath()

// app/api/revalidate/route.js
import { revalidatePath } from 'next/cache';

export async function POST(req) {
  const { path } = await req.json();
  revalidatePath(path); // Invalidate and re-generate this route
  return Response.json({ revalidated: true, path });
}

Example: Using revalidateTag()

You can tag your fetches to group them logically:

// app/products/page.js
export default async function ProductsPage() {
  const res = await fetch('https://api.example.com/products', {
    next: { tags: ['products'] },
  });
  const products = await res.json();
  return <ProductList data={products} />;
}

// app/actions/revalidateProducts.js
'use server';
import { revalidateTag } from 'next/cache';

export async function revalidateProducts() {
  revalidateTag('products'); // Revalidate all fetches with 'products' tag
}

You can trigger revalidateProducts() from CMS webhooks or admin dashboards.


Comparing Caching Mechanisms in the App Router

Cache TypeDescriptionLifecycleExample
Request MemoizationDeduplicates fetch calls during the same renderRequest-levelSame fetch() inside one render won’t re-run
Data CacheStores fetch responses between requestsTime-based or manual revalidationControlled via revalidate or tags
Full Route CacheStores the fully rendered routeCleared on route revalidationAffected by revalidatePath()

Together, these layers optimize performance and give developers full control over how and when data stays fresh.


Pages Router Revalidation (Legacy)

If you’re still using the Pages Router, you’ll continue to use getStaticProps and API routes for cache revalidation.

Time-Based Example

export async function getStaticProps() {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return {
    props: { posts },
    revalidate: 60,
  };
}

On-Demand Example

export default async function handler(req, res) {
  try {
    await res.revalidate('/blog/my-post');
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).json({ message: 'Error revalidating' });
  }
}

Best Practices for Cache Revalidation

  1. Use fetch options wisely — Add { next: { revalidate, tags } } for precise control.
  2. Tag related data sources — Makes revalidation scalable via revalidateTag().
  3. Secure on-demand endpoints with tokens or auth checks.
  4. Avoid redundant fetches — Rely on Next.js’s built-in memoization.
  5. Combine methods — Use time-based revalidation for regular updates and manual revalidation for instant updates.

Conclusion

Revalidation in Next.js has become more modular, declarative, and powerful with the App Router. You can now manage freshness at both the data-fetch and route-render levels using built-in functions.

Quick Summary

Router TypeMethodAPIIdeal Use
App RouterTime-Basedfetch(..., { next: { revalidate } })Periodic data refresh
App RouterOn-DemandrevalidatePath(), revalidateTag()Real-time updates
Pages RouterTime-BasedgetStaticProps + revalidateStatic ISR sites
Pages RouterOn-Demandres.revalidate()Manual cache invalidation

With these tools, you can confidently manage caching and revalidation in any Next.js version — ensuring both speed and freshness across your app.


Next Steps: