typescriptadvanced

Caching Strategies in Next.js

Master Next.js caching with fetch cache, unstable_cache, revalidatePath, and revalidateTag patterns.

typescript
import { unstable_cache } from 'next/cache';
import { revalidatePath, revalidateTag } from 'next/cache';

// 1. Fetch with time-based revalidation
async function getProducts() {
  const res = await fetch('https://api.example.com/products', {
    next: { revalidate: 3600 }, // Revalidate every hour
  });
  return res.json();
}

// 2. Fetch with tag-based revalidation
async function getProduct(id: string) {
  const res = await fetch(`https://api.example.com/products/${id}`, {
    next: { tags: [`product-${id}`, 'products'] },
  });
  return res.json();
}

// 3. Cache non-fetch data (database queries, computations)
const getCachedUser = unstable_cache(
  async (userId: string) => {
    // Simulated DB query
    console.log(`DB query for user: ${userId}`);
    return { id: userId, name: 'Alice', role: 'admin' };
  },
  ['user'], // Cache key prefix
  {
    revalidate: 900,          // 15 minutes
    tags: ['users'],           // Tag for manual invalidation
  },
);

// 4. Cache with dynamic keys
const getCachedAnalytics = unstable_cache(
  async (period: string) => {
    return { period, views: 12345, visitors: 6789 };
  },
  ['analytics'],
  { revalidate: 300, tags: ['analytics'] },
);

// 5. Server Action for revalidation
async function updateProduct(formData: FormData) {
  'use server';

  const id = formData.get('id') as string;
  // ... update in DB

  // Revalidate specific product cache
  revalidateTag(`product-${id}`);

  // Or revalidate by path
  revalidatePath(`/products/${id}`);

  // Or revalidate all products
  // revalidateTag('products');
}

// 6. No-cache for dynamic data
async function getCurrentUser() {
  const res = await fetch('https://api.example.com/me', {
    cache: 'no-store', // Always fresh
  });
  return res.json();
}

// Page component using cached data
export default async function ProductPage({ params }: { params: { id: string } }) {
  const [product, user, analytics] = await Promise.all([
    getProduct(params.id),
    getCachedUser('user-1'),
    getCachedAnalytics('7d'),
  ]);

  return (
    <div>
      <h1>{product.name}</h1>
      <p>Welcome, {user.name}</p>
      <p>Views: {analytics.views}</p>
    </div>
  );
}

Sponsored

Vercel

Use Cases

  • ISR page caching
  • Database query caching
  • On-demand cache invalidation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.