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.

Tips

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

Warnings

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

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.

FAQ