Caching Strategies

Master advanced caching strategies in Next.js — combining SSG, ISR, SSR, and client caching for optimal performance across different content types.

Last reviewed: January 2025

·Next.js 13Next.js 14Next.js 15
✅
Quick Understanding: The right caching strategy depends on the content type. Static content → SSG. Frequently updated content → ISR. User-specific content → SSR. Real-time content → SSR with short TTL or client-side. Mixing strategies in one app is normal and expected.

🔍 What Is It?

Caching strategy selection in Next.js means choosing the right combination of:

  • →SSG — build-time static, served from CDN forever
  • →ISR — static with time-based or on-demand revalidation
  • →SSR — server-rendered per request, optionally cached at CDN
  • →Client-side — fetched in the browser, cached by SWR/TanStack Query
  • →unstable_cache — server-side function cache with TTL and tags
  • →Redis — shared cache for multi-instance deployments

🎯 Why Do We Need It?

No single caching strategy fits all content:

  • →A blog post (changes monthly) → SSG is perfect
  • →A product price (changes daily) → ISR with 1hr revalidation
  • →A user's order history (changes per user) → SSR
  • →Live stock prices (changes per second) → SSR + client polling

Mixing strategies in one app is the norm, not the exception.

🏗️ What Happens in Real Projects?

Caching strategy map for an e-commerce platform:

RouteStrategyReason
/ (homepage)ISR 1hr + on-demandFeatured products change
/productsISR 1hrCatalog changes infrequently
/products/[slug]SSG + ISR on-demandIndividual products
/blog/[slug]SSG + on-demandPublished once
/searchSSRDynamic query
/cartSSRUser-specific
/dashboardSSRAuth + user-specific
/live-pricesSSR + client pollReal-time

⚙️ How It Works

Cache decision tree:

  1. Is the content the same for all users? → Yes: SSG/ISR. No: SSR
  2. How often does it change? → Never: SSG. Occasionally: ISR. Always: SSR
  3. Can you tolerate stale content? → Yes: ISR. No: SSR
  4. Is it triggered by user action? → On-demand ISR or SSR
  5. Is it real-time? → SSR + client polling or WebSocket

💻 Example

Mixed Caching Strategy Implementation — Different caching strategies in the same Next.js application.

ts
// lib/data/products.ts
import { unstable_cache } from 'next/cache';
import { db } from '@/lib/db';

// Cache DB query with ISR semantics
export const getProducts = unstable_cache(
  async (category?: string) => {
    return db.product.findMany({
      where: category ? { category } : undefined,
      orderBy: { createdAt: 'desc' },
    });
  },
  ['products'], // Cache key prefix
  {
    revalidate: 3600,       // 1 hour TTL
    tags: ['products'],     // Tag for on-demand invalidation
  }
);

// Specific product with its own tag
export const getProduct = unstable_cache(
  async (slug: string) => {
    return db.product.findUnique({ where: { slug } });
  },
  ['product'],
  {
    revalidate: 3600,
    tags: ['products', `product-${slug}`], // Note: slug not available here
    // Use a factory function pattern for per-item tags:
  }
);

// Factory pattern for per-item caching
export function getCachedProduct(slug: string) {
  return unstable_cache(
    async () => db.product.findUnique({ where: { slug } }),
    [`product-${slug}`],
    { revalidate: 3600, tags: ['products', `product-${slug}`] }
  )();
}

🤔 Common Confusions

❓ Can I use ISR for user-specific pages?

No — ISR caches the same HTML for all users. A user-specific page cached with ISR would show one user's data to all users. Use SSR for any content that differs between users. The rule: ISR for public content, SSR for private content.

❓ Is unstable_cache production-ready?

Yes — despite the "unstable" prefix (a legacy naming issue), unstable_cache is widely used in production. It is the recommended way to add ISR-style caching to database queries. The API may change in future Next.js versions, but it is stable enough for production use.

❓ How do I choose between ISR revalidate time and on-demand revalidation?

Use both together. Time-based revalidation is the safety net — ensures content is eventually fresh even if the webhook fails. On-demand revalidation is the primary mechanism for immediate updates. Example: revalidate: 3600 (1hr safety net) + on-demand webhook on CMS publish.

🚨 Common Production Issues

Cache stampede on popular ISR pages — when a cached page expires and receives thousands of simultaneous requests, all trigger a rebuild simultaneously. Use a distributed lock (Redis) or configure Vercel's ISR cache to handle this automatically.

unstable_cache not shared between serverless instances — each serverless function instance has its own in-memory cache. For shared caching across instances, use Redis with a custom cache handler.

Mixing SSR and ISR on the same route — a route cannot be both SSR and ISR. If any part of the route is dynamic (reads cookies), the entire route is SSR. Design your routes to be either fully static or fully dynamic.

✅ Best Practices

Create a caching strategy document before building — map each route to its strategy

Use both time-based and on-demand revalidation for important content

Tag all cached content for precise on-demand invalidation

Use unstable_cache for DB queries that should have ISR semantics

Test your caching strategy with next build and production traffic patterns

🏛️ Architect's Note

Senior Architect's Perspective

Caching is the most impactful performance lever in a Next.js application. A well-designed caching architecture can reduce server costs by 90% and improve response times by 10x. Invest time upfront in designing the caching strategy for each content type — it is much harder to retrofit caching into an existing app.

💬 Frequently Asked Questions

How do I implement a Redis cache in Next.js?

Use Upstash Redis (edge-compatible, serverless-friendly) or ioredis (Node.js only). Wrap your data access functions with Redis get/set: const cached = await redis.get(key); if (cached) return JSON.parse(cached); const data = await db.query(); await redis.setex(key, 3600, JSON.stringify(data)); return data.

Should I cache at the Next.js level or the database level?

Both — they serve different purposes. Database-level caching (connection pooling, query caching) reduces DB load. Next.js caching (Data Cache, unstable_cache) reduces the number of DB calls from your app. Database-level caching is transparent; Next.js caching is explicit and gives you control over TTL and invalidation.

How do I test that my caching is working correctly?

Run next build and check the route summary — each route shows its rendering mode. Add cache headers to your responses and inspect them in the browser Network tab. Use console.log in data fetching functions to verify they are not called on every request.

🎯 Key Takeaways

1Match caching strategy to content type: static → SSG, changing → ISR, user-specific → SSR
2Use both time-based and on-demand revalidation for important content
3unstable_cache adds ISR semantics to database queries
4ISR is for public content only — never for user-specific data
5Design caching strategy upfront — it is hard to retrofit
6Tag all cached content for precise on-demand invalidation

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these Next.js references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide Next.js learning path

Browse the full Next.js topic collection and learning order on SplashRide.

Next.js Interview Prep

Practice Next.js interview questions and scenario-based answers.

Frontend Engineer Career Path

A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.

Full Stack Java Developer Career Path

A guided route through frontend foundations, Java backend engineering, delivery workflows, and interview readiness.

Incremental Static Regeneration (ISR)

Master ISR — the Next.js feature that gives you static performance with dynamic freshness. Learn time-based and on-demand revalidation for real production systems.

Caching in Next.js

Understand all four caching layers in Next.js — Request Memoisation, Data Cache, Full Route Cache, and Router Cache — and how they interact.