JS Guide
HomeQuestionsTopicsCompaniesResources
BookmarksSearch

Built for developers preparing for JavaScript, React & TypeScript interviews.

ResourcesQuestionsSupport
HomeQuestionsSearchProgress
HomeQuestionsnextjs
Prev

Learn the concept

Caching & Revalidation

nextjs
senior
caching

How does caching and revalidation work in Next.js?

nextjs
caching
revalidation
isr
data-cache
router-cache
performance
Quick Answer

Next.js has multiple cache layers: Request Memoization (dedupes fetch calls per render), Data Cache (persists fetch results across requests), Full Route Cache (caches rendered routes), and Router Cache (client-side cache). Revalidation can be time-based (revalidate option), on-demand (revalidatePath/revalidateTag), or opt-out entirely with cache: 'no-store'.

Detailed Explanation

The Four Cache Layers

1. Request Memoization

  • Scope: Single render pass
  • Purpose: Deduplicate identical fetch requests
  • How: React extends fetch to memoize same URL + options
  • Benefit: Call getUser() in multiple components, only one request made

2. Data Cache

  • Scope: Persistent across requests and deployments
  • Purpose: Store fetch results on server
  • Control: fetch() options: cache, next.revalidate, next.tags
  • Revalidation: Time-based or on-demand

3. Full Route Cache

  • Scope: Build time + runtime (ISR)
  • Purpose: Cache rendered HTML and RSC payload
  • Behavior: Static routes cached at build, dynamic routes rendered per request
  • Invalidation: When Data Cache is revalidated

4. Router Cache (Client-side)

  • Scope: Browser session
  • Purpose: Cache visited routes for instant back/forward navigation
  • Duration: 30 seconds (dynamic), 5 minutes (static)
  • Invalidation: router.refresh(), revalidatePath, cookie changes

Next.js 16: Opt-In Caching with "use cache"

Next.js 16 introduces an opt-in caching model. Instead of caching by default, you explicitly opt in with the "use cache" directive:

TypeScript
// Mark a function for caching
async function getProducts() {
  'use cache';
  cacheTag('products');
  cacheLife('hours');
  return await db.product.findMany();
}

cacheLife Profiles

Built-in profiles control cache duration: 'seconds', 'minutes', 'hours', 'days', 'weeks', 'max'.

cacheTag() and revalidateTag()

Use cacheTag('name') inside "use cache" functions, then revalidateTag('name', 'max') to invalidate. Note: revalidateTag requires two arguments (tag and scope).

updateTag() for Immediate Updates

Use updateTag() when you need immediate cache updates without waiting for revalidation.

unstable_cache Deprecation

unstable_cache is on a deprecation path. Prefer the "use cache" + cacheTag() pattern for new code.

Cache Control Options

TypeScript
// No caching - always fresh
fetch(url, { cache: 'no-store' })
 
// Cache indefinitely (default for GET)
fetch(url, { cache: 'force-cache' })
 
// Revalidate every 60 seconds
fetch(url, { next: { revalidate: 60 } })
 
// Tag for on-demand revalidation
fetch(url, { next: { tags: ['posts'] } })

On-Demand Revalidation

  • revalidatePath('/blog') - Revalidate specific path
  • revalidatePath('/blog', 'layout') - Revalidate layout and children
  • revalidateTag('posts', 'max') - Revalidate all fetches with tag (v16 requires two args)

Route Segment Config

TypeScript
// Force dynamic rendering
export const dynamic = 'force-dynamic';
 
// Force static (error if dynamic functions used)
export const dynamic = 'force-static';
 
// Revalidate entire route every hour
export const revalidate = 3600;

Code Examples

Fetch Caching OptionsTSX
// app/products/page.tsx

// Cached indefinitely (default) - good for static data
async function getCategories() {
  const res = await fetch('https://api.example.com/categories');
  return res.json();
}

// Time-based revalidation - good for data that changes periodically
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    next: { revalidate: 3600 }, // Revalidate every hour
  });
  return res.json();
}

// No caching - good for real-time data
async function getInventory(productId: string) {
  const res = await fetch(`https://api.example.com/inventory/${productId}`, {
    cache: 'no-store', // Always fresh
  });
  return res.json();
}

// Tagged for on-demand revalidation
async function getProductDetails(id: string) {
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: { 
      tags: [`product-${id}`, 'products'], // Multiple tags
      revalidate: 86400, // Also time-based (1 day)
    },
  });
  return res.json();
}

Real-World Applications

Use Cases

CMS Content Revalidation

Using cacheTag with webhook-triggered revalidateTag to instantly update cached pages when CMS editors publish content

E-Commerce Price Updates

Using cacheLife profiles for product pages (hours) vs inventory (no cache) to balance performance with freshness

Multi-Layer Cache Strategy

Designing cache layers: CDN for static assets, use cache for data, and Router Cache for client-side navigation

Mini Projects

Cache Strategy Demonstrator

intermediate

Build an app showing use cache with different cacheLife profiles, cacheTag for on-demand revalidation, and monitoring cache behavior

Webhook-Based Revalidation System

advanced

Create a CMS integration that receives webhooks and calls revalidateTag to invalidate specific cached content

Industry Examples

Contentful

Integrates with Next.js caching via webhook-triggered revalidation when content is published or updated

Shopify

Uses tag-based cache invalidation in Hydrogen for updating product pages when inventory or pricing changes

Resources

Caching in Next.js

docs

revalidatePath API Reference

docs

Data Fetching, Caching, and Revalidating

docs

Related Questions

Explain the different rendering strategies in Next.js (SSR, SSG, ISR) and when to use each approach.

senior
rendering

How does data fetching work in Next.js?

mid
data-fetching
Previous
What performance optimization techniques does Next.js provide?
Prev