Technology · Next.js

Next.js Caching and Revalidation

Understand ISR, revalidatePath, revalidateTag, and caching strategies for optimal performance.

TL;DR
  1. 01Use revalidate to set cache time and update pages periodically.
  2. 02Use revalidatePath to manually update specific pages.
  3. 03Use revalidateTag for tag-based cache invalidation.

Static Generation with Revalidation

  • Set a revalidate time to cache pages and periodically rebuild.
    export const revalidate = 3600; // Revalidate every hour
    
    export default async function Page() {
      const data = await fetch("https://api.example.com/data");
      return <div>{data}</div>;
    }
  • Pages are cached and served statically until the time expires.
  • When the time expires, the next request rebuilds the page on demand.
  • Great for content that changes infrequently but needs eventual freshness.
  • revalidate is measured in seconds.

On-Demand Revalidation with revalidatePath

  • Manually revalidate specific paths when data changes.
    // app/api/revalidate/route.ts
    import { revalidatePath } from "next/cache";
    
    export async function POST() {
      revalidatePath("/blog/[slug]");
      revalidatePath("/blog");
      return { revalidated: true };
    }
  • Call this endpoint after updating content in your CMS or database.
  • Paths are immediately regenerated on the next request.
  • Pass exact paths or patterns for multiple routes.
    revalidatePath("/blog/[slug]", "page");     // All dynamic blog posts
    revalidatePath("/", "layout");             // Entire site using this layout
  • Use webhooks from your CMS to trigger revalidation automatically.

Tag-Based Revalidation

  • Use tags to group related cache entries for bulk invalidation.
    // app/page.tsx
    export default async function Page() {
      const res = await fetch("https://api.example.com/posts", {
        next: { tags: ["posts"] }
      });
      const posts = await res.json();
      return <div>{posts}</div>;
    }
  • Revalidate all requests with a specific tag at once.
    // app/api/revalidate/route.ts
    import { revalidateTag } from "next/cache";
    
    export async function POST(request) {
      const tag = request.nextUrl.searchParams.get("tag");
      revalidateTag(tag); // "posts"
      return { revalidated: true };
    }
  • Useful for invalidating multiple related pages together.
    // Invalidate all blog-related content
    revalidateTag("blog");

Fetch Cache Control

  • Configure fetch caching behavior for API calls.
    // Cache for 1 hour
    const res = await fetch("https://api.example.com/data", {
      next: { revalidate: 3600 }
    });
    
    // Never cache
    const res = await fetch("https://api.example.com/data", {
      cache: "no-store"
    });
    
    // Cache indefinitely (default)
    const res = await fetch("https://api.example.com/data", {
      next: { revalidate: false }
    });
  • Fetch requests are cached by default in server components.
  • Set no-store to always fetch fresh data on every request.
  • Combine with tags for flexible cache invalidation.

Caching Strategies

  • Static generation with periodic revalidation for blog posts.
    export const revalidate = 86400; // Daily
    
    export async function generateStaticParams() {
      const posts = await getPosts();
      return posts.map(p => ({ slug: p.slug }));
    }
    
    export default async function Post({ params }) {
      const post = await getPost(params.slug);
      return <article>{post.content}</article>;
    }
  • Dynamic rendering with revalidateTag for content that updates frequently.
    export default async function Dashboard() {
      const data = await fetch("https://api.example.com/data", {
        next: { tags: ["dashboard"] }
      });
      return <div>{data}</div>;
    }
  • Use manual revalidation with webhooks from your CMS.
  • Choose based on how often content changes and performance needs.

Tip: Use tags for related content so you can invalidate all affected pages at once when content changes in your CMS or database.

Warning: Set appropriate revalidate times for your use case — too short defeats caching benefits, too long means stale content for users.

Next.js App RouterNext.js Compiler